diff --git a/src/page-components/pattern-library/PatternDetail.module.css b/src/page-components/pattern-library/PatternDetail.module.css index cfaec95..33c9845 100644 --- a/src/page-components/pattern-library/PatternDetail.module.css +++ b/src/page-components/pattern-library/PatternDetail.module.css @@ -3,15 +3,58 @@ flex: 2; border-radius: calc(var(--border-radius) * 2); background-color: var(--color-surface-container); + padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2); display: flex; flex-direction: column; align-items: stretch; gap: calc(var(--spacing) * 2); + .control_panel { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 2); + .button_row { + display: flex; + flex-direction: row; + align-items: center; + gap: calc(var(--spacing) * 2); + } + } + .detail_panel { + flex: 1; + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 2); + .detail_row { + display: flex; + flex-direction: row; + align-items: center; + gap: calc(var(--spacing) * 2); + .detail_unit { + display: flex; + flex-direction: row; + align-items: center; + gap: calc(var(--spacing)); + label { + min-width: 8em; + flex: 0 0 8em; + text-align: right; + } + .content { + flex: 1 1; + } + } + } + } + .preview_panel { + min-height: 140px; + flex: 0 0; + } } .empty_promption { flex: 2; border-radius: calc(var(--border-radius) * 2); background-color: var(--color-surface-container); + padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2); display: flex; flex-direction: column; align-items: stretch; diff --git a/src/page-components/pattern-library/PatternDetail.tsx b/src/page-components/pattern-library/PatternDetail.tsx index bf9c4c0..dc02467 100644 --- a/src/page-components/pattern-library/PatternDetail.tsx +++ b/src/page-components/pattern-library/PatternDetail.tsx @@ -1,6 +1,17 @@ -import { useAtomValue } from 'jotai'; -import { FC } from 'react'; -import { CurrentPatternAtom, Pattern } from '../../context/Patterns'; +import { invoke } from '@tauri-apps/api/core'; +import { ask } from '@tauri-apps/plugin-dialog'; +import dayjs from 'dayjs'; +import { useAtomValue, useSetAtom } from 'jotai'; +import { FC, useCallback, useMemo } from 'react'; +import { NotificationType, useNotification } from '../../components/Notifications'; +import PatternPreview from '../../components/PatternPreview'; +import { + CurrentPatternAtom, + Pattern, + PatternsAtom, + SelectedPatternIdAtom, + totalDuration, +} from '../../context/Patterns'; import styles from './PatternDetail.module.css'; const EmptyPromption: FC = () => { @@ -15,11 +26,80 @@ const EmptyPromption: FC = () => { }; const Detail: FC<{ pattern: Pattern }> = ({ pattern }) => { + const { showToast } = useNotification(); + const refreshPatterns = useSetAtom(PatternsAtom); + const resetSelected = useSetAtom(SelectedPatternIdAtom); + const patternDuration = useMemo(() => totalDuration(pattern), [pattern]); + const createTime = useMemo( + () => dayjs(pattern.createdAt).format('YYYY-MM-DD HH:mm:ss'), + [pattern], + ); + + const handleDeleteAction = useCallback(async () => { + try { + const answer = await ask( + `The pattern ${pattern.name} will be deleted, and cannot be revoked. Are you sure?`, + { + title: 'Confirm action', + kind: 'warning', + }, + ); + if (answer) { + await invoke('remove_pattern', { patternId: pattern.id }); + showToast(NotificationType.SUCCESS, 'Pattern deleted.'); + refreshPatterns(); + resetSelected(null); + } + } catch (e) { + console.error('[delete pattern]', e); + showToast(NotificationType.ERROR, 'Failed to delete pattern. Please try again.'); + } + }, [pattern]); + return (