list redesign

This commit is contained in:
Alex Tran
2026-05-20 22:33:09 -05:00
parent 9ea4a03f21
commit 3005ff0cc4
2 changed files with 74 additions and 111 deletions
+1
View File
@@ -2255,6 +2255,7 @@
"step_delete_confirm": "Are you sure you want to delete this step?",
"step_details": "Step details",
"steps": "Steps",
"steps_count": "{count, plural, one {# step} other {# steps}}",
"stop_casting": "Stop casting",
"stop_motion_photo": "Stop Motion Photo",
"stop_photo_sharing": "Stop sharing your photos?",
+73 -111
View File
@@ -4,27 +4,11 @@
import UserPageLayout from '$lib/components/layouts/UserPageLayout.svelte';
import OnEvents from '$lib/components/OnEvents.svelte';
import EmptyPlaceholder from '$lib/components/shared-components/EmptyPlaceholder.svelte';
import { pluginManager } from '$lib/managers/plugin-manager.svelte';
import { Route } from '$lib/route';
import { getWorkflowActions, getWorkflowsActions, getWorkflowShowSchemaAction } from '$lib/services/workflow.service';
import { getWorkflowForShare, type WorkflowResponseDto } from '@immich/sdk';
import {
Button,
Card,
CardBody,
CardDescription,
CardHeader,
CardTitle,
CodeBlock,
Container,
IconButton,
Link,
MenuItemType,
menuManager,
Text,
VStack,
} from '@immich/ui';
import { mdiClose, mdiDotsVertical } from '@mdi/js';
import { Button, CodeBlock, Container, Icon, IconButton, MenuItemType, menuManager } from '@immich/ui';
import { mdiClose, mdiDotsVertical, mdiFlashOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
import { SvelteSet } from 'svelte/reactivity';
import type { PageData } from './$types';
@@ -55,12 +39,6 @@
return labels[triggerType] || triggerType;
};
const formatTimestamp = (createdAt: string) =>
new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'short',
}).format(new Date(createdAt));
const showWorkflowMenu = (event: MouseEvent, workflow: WorkflowResponseDto) => {
const { ToggleEnabled, Edit, Delete } = getWorkflowActions($t, workflow);
void menuManager.show({
@@ -93,12 +71,6 @@
<OnEvents {onWorkflowCreate} {onWorkflowUpdate} {onWorkflowDelete} />
{#snippet chipItem(title: string)}
<span class="rounded-xl border border-gray-200/80 bg-light px-3 py-1.5 text-sm dark:border-gray-600">
<span class="font-medium text-dark">{title}</span>
</span>
{/snippet}
<UserPageLayout title={data.meta.title} actions={[Create]} scrollbar={false}>
<section class="flex place-content-center sm:mx-4">
<Container center size="large" class="pb-28">
@@ -112,95 +84,85 @@
class="mx-auto mt-10"
/>
{:else}
<div class="my-6 grid gap-6">
<div class="my-6 flex flex-col gap-3">
{#each workflows as workflow (workflow.id)}
<Card class="border border-light-200">
<CardHeader
class={`flex flex-row gap-4 px-8 py-6 sm:items-center sm:gap-6 ${
workflow.enabled
? 'bg-linear-to-r from-green-50 to-white dark:from-green-800/50 dark:to-green-950/45'
: 'bg-neutral-50 dark:bg-neutral-900'
}`}
<div
class="group border-outline overflow-hidden rounded-2xl border transition-colors hover:bg-primary/5 dark:border-neutral-800"
>
<a
href={Route.viewWorkflow({ id: workflow.id })}
class="flex items-center gap-4 px-5 py-4"
class:opacity-55={!workflow.enabled}
>
<div class="flex-1">
<div class="flex items-center gap-3">
<span class="rounded-full {workflow.enabled ? 'size-3 bg-success' : 'size-3 rounded-full bg-muted'}"
></span>
<CardTitle>
<Link href={Route.viewWorkflow({ id: workflow.id })}>{workflow.name || $t('workflow')}</Link>
</CardTitle>
<div
class={`flex size-11 shrink-0 items-center justify-center rounded-xl ${
workflow.enabled
? 'bg-immich-primary/10 text-immich-primary dark:bg-immich-dark-primary/15 dark:text-immich-dark-primary'
: 'bg-gray-100 text-gray-400 dark:bg-gray-800 dark:text-gray-500'
}`}
>
<Icon icon={mdiFlashOutline} size="20" />
</div>
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<h3
class="truncate text-base font-semibold text-dark group-hover:text-immich-primary dark:group-hover:text-immich-dark-primary"
>
{workflow.name || $t('workflow')}
</h3>
{#if !workflow.enabled}
<span
class="shrink-0 rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-medium tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-400"
>
{$t('disabled')}
</span>
{/if}
</div>
<p class="mt-0.5 truncate text-sm text-gray-500 dark:text-gray-400">
{getTriggerLabel(workflow.trigger)} · {$t('steps_count', {
values: { count: workflow.steps.length },
})}
</p>
{#if workflow.description}
<CardDescription class="mt-1 text-sm">{workflow.description}</CardDescription>
<p class="mt-1 truncate text-xs text-gray-500/80 dark:text-gray-500">
{workflow.description}
</p>
{/if}
</div>
<div class="flex items-center gap-4">
<div class="hidden text-right sm:block">
<Text size="tiny">{$t('created_at')}</Text>
<Text size="small" fontWeight="medium">
{formatTimestamp(workflow.createdAt)}
</Text>
<IconButton
shape="round"
variant="ghost"
color="secondary"
icon={mdiDotsVertical}
aria-label={$t('menu')}
onclick={(event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
showWorkflowMenu(event, workflow);
}}
/>
</a>
{#if expandedIds.has(workflow.id)}
{#await getWorkflowForShare({ id: workflow.id }) then result}
<div class="border-t border-gray-200 p-4 dark:border-gray-800">
<CodeBlock code={JSON.stringify(result, null, 2)} lineNumbers />
<Button
class="mt-2"
leadingIcon={mdiClose}
fullWidth
variant="ghost"
color="secondary"
onclick={() => toggleExpanded(workflow.id)}
>
{$t('close')}
</Button>
</div>
<IconButton
shape="round"
variant="ghost"
color="secondary"
icon={mdiDotsVertical}
aria-label={$t('menu')}
onclick={(event: MouseEvent) => showWorkflowMenu(event, workflow)}
/>
</div>
</CardHeader>
<CardBody class="space-y-6">
<div class="grid gap-4 md:grid-cols-3">
<!-- Trigger Section -->
<div class="rounded-2xl border border-light-200 bg-light-50 p-4">
<div class="mb-3">
<Text size="tiny" color="muted" fontWeight="medium">{$t('trigger')}</Text>
</div>
{@render chipItem(getTriggerLabel(workflow.trigger))}
</div>
<!-- Actions Section -->
<div class="rounded-2xl border border-light-200 bg-light-50 p-4">
<div class="mb-3">
<Text size="tiny" color="muted" fontWeight="medium">{$t('steps')}</Text>
</div>
<div>
{#if workflow.steps.length === 0}
<span class="text-sm text-light-600">
{$t('no_steps')}
</span>
{:else}
<div class="flex flex-wrap gap-2">
{#each workflow.steps as step, i (i)}
{@render chipItem(pluginManager.getMethodLabel(step.method))}
{/each}
</div>
{/if}
</div>
</div>
</div>
{#if expandedIds.has(workflow.id)}
{#await getWorkflowForShare({ id: workflow.id }) then result}
<VStack gap={2} class="w-full rounded-2xl border border-light-200 bg-light-50 p-4">
<CodeBlock code={JSON.stringify(result, null, 2)} lineNumbers />
<Button
leadingIcon={mdiClose}
fullWidth
variant="ghost"
color="secondary"
onclick={() => toggleExpanded(workflow.id)}>{$t('close')}</Button
>
</VStack>
{/await}
{/if}
</CardBody>
</Card>
{/await}
{/if}
</div>
{/each}
</div>
{/if}