style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
@@ -19,59 +19,59 @@
|
||||
* T-04-06 XSS guard: all text is static or plain-text JSX children (no dangerouslySetInnerHTML).
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { createList } from '../api/listsClient.js'
|
||||
import type { ListsResponse } from '../api/listsClient.js'
|
||||
import { useListsStore } from '../store/listsStore.js'
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { createList } from '../api/listsClient.js';
|
||||
import type { ListsResponse } from '../api/listsClient.js';
|
||||
import { useListsStore } from '../store/listsStore.js';
|
||||
|
||||
export function CreateListSheet() {
|
||||
const isOpen = useListsStore((s) => s.createListSheetOpen)
|
||||
const setOpen = useListsStore((s) => s.setCreateListSheetOpen)
|
||||
const queryClient = useQueryClient()
|
||||
const isOpen = useListsStore((s) => s.createListSheetOpen);
|
||||
const setOpen = useListsStore((s) => s.setCreateListSheetOpen);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [name, setName] = useState('')
|
||||
const [isShared, setIsShared] = useState(true) // D-01: default shared
|
||||
const [attemptedEmpty, setAttemptedEmpty] = useState(false)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [name, setName] = useState('');
|
||||
const [isShared, setIsShared] = useState(true); // D-01: default shared
|
||||
const [attemptedEmpty, setAttemptedEmpty] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Auto-focus name input when sheet opens
|
||||
useEffect(() => {
|
||||
if (isOpen && inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
inputRef.current.focus();
|
||||
// Reset form state on open
|
||||
setName('')
|
||||
setIsShared(true)
|
||||
setAttemptedEmpty(false)
|
||||
setName('');
|
||||
setIsShared(true);
|
||||
setAttemptedEmpty(false);
|
||||
}
|
||||
}, [isOpen])
|
||||
}, [isOpen]);
|
||||
|
||||
// Escape key listener
|
||||
useEffect(() => {
|
||||
if (!isOpen) return
|
||||
if (!isOpen) return;
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
handleClose()
|
||||
handleClose();
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', onKeyDown)
|
||||
return () => document.removeEventListener('keydown', onKeyDown)
|
||||
}, [isOpen]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
};
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
return () => document.removeEventListener('keydown', onKeyDown);
|
||||
}, [isOpen]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
setName('')
|
||||
setIsShared(true)
|
||||
setAttemptedEmpty(false)
|
||||
}
|
||||
setOpen(false);
|
||||
setName('');
|
||||
setIsShared(true);
|
||||
setAttemptedEmpty(false);
|
||||
};
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: (payload: { name: string; isShared: boolean }) => createList(payload),
|
||||
onMutate: async (payload) => {
|
||||
// Optimistic insert: add a temporary entry to the lists cache
|
||||
await queryClient.cancelQueries({ queryKey: ['lists'] })
|
||||
const previous = queryClient.getQueryData<ListsResponse>(['lists'])
|
||||
const tempId = -Date.now() // negative temp ID so it won't collide with real IDs
|
||||
await queryClient.cancelQueries({ queryKey: ['lists'] });
|
||||
const previous = queryClient.getQueryData<ListsResponse>(['lists']);
|
||||
const tempId = -Date.now(); // negative temp ID so it won't collide with real IDs
|
||||
queryClient.setQueryData<ListsResponse>(['lists'], (old) => ({
|
||||
lists: [
|
||||
...(old?.lists ?? []),
|
||||
@@ -84,39 +84,38 @@ export function CreateListSheet() {
|
||||
doneCount: 0,
|
||||
},
|
||||
],
|
||||
}))
|
||||
return { previous }
|
||||
}));
|
||||
return { previous };
|
||||
},
|
||||
onError: (_err, _vars, context) => {
|
||||
// Rollback on error
|
||||
if (context?.previous) {
|
||||
queryClient.setQueryData(['lists'], context.previous)
|
||||
queryClient.setQueryData(['lists'], context.previous);
|
||||
}
|
||||
},
|
||||
onSettled: () => {
|
||||
// Always invalidate to get canonical server state; fire-and-forget (React Query handles cache update)
|
||||
void queryClient.invalidateQueries({ queryKey: ['lists'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['lists'] });
|
||||
},
|
||||
onSuccess: () => {
|
||||
handleClose()
|
||||
handleClose();
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
if (!name.trim()) {
|
||||
setAttemptedEmpty(true)
|
||||
return
|
||||
setAttemptedEmpty(true);
|
||||
return;
|
||||
}
|
||||
mutation.mutate({ name: name.trim(), isShared })
|
||||
}
|
||||
mutation.mutate({ name: name.trim(), isShared });
|
||||
};
|
||||
|
||||
if (!isOpen) return null
|
||||
if (!isOpen) return null;
|
||||
|
||||
const isNameEmpty = !name.trim()
|
||||
const inputBorderColor = attemptedEmpty && isNameEmpty
|
||||
? 'var(--color-destructive)'
|
||||
: 'var(--color-border)'
|
||||
const isNameEmpty = !name.trim();
|
||||
const inputBorderColor =
|
||||
attemptedEmpty && isNameEmpty ? 'var(--color-destructive)' : 'var(--color-border)';
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -189,8 +188,8 @@ export function CreateListSheet() {
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
setName(e.target.value)
|
||||
if (e.target.value.trim()) setAttemptedEmpty(false)
|
||||
setName(e.target.value);
|
||||
if (e.target.value.trim()) setAttemptedEmpty(false);
|
||||
}}
|
||||
placeholder="e.g. Groceries"
|
||||
maxLength={255}
|
||||
@@ -294,9 +293,8 @@ export function CreateListSheet() {
|
||||
minHeight: '48px',
|
||||
background: isNameEmpty ? 'var(--color-surface-dim)' : 'var(--color-member-0)',
|
||||
color: isNameEmpty ? 'var(--color-text-muted)' : '#ffffff',
|
||||
border: attemptedEmpty && isNameEmpty
|
||||
? '1px solid var(--color-destructive)'
|
||||
: 'none',
|
||||
border:
|
||||
attemptedEmpty && isNameEmpty ? '1px solid var(--color-destructive)' : 'none',
|
||||
borderRadius: 'var(--space-1)',
|
||||
fontSize: 'var(--text-label-size)',
|
||||
fontWeight: 600,
|
||||
@@ -332,5 +330,5 @@ export function CreateListSheet() {
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user