improvement: all demo bugs fixed. give me more baby.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState, type KeyboardEvent } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
import { Eye, EyeOff, Send } from 'lucide-react';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { Button } from '@/components/ui/shared/Button';
|
||||
import type { LabCaseComment } from '@/types/cases';
|
||||
|
||||
interface LabCaseCommentsPanelProps {
|
||||
@@ -15,6 +14,13 @@ interface LabCaseCommentsPanelProps {
|
||||
onPost: (body: string, visibleToClinic?: boolean) => Promise<LabCaseComment>;
|
||||
onToggleVisibility?: (commentId: string, visible: boolean) => Promise<LabCaseComment>;
|
||||
onError?: (message: string) => void;
|
||||
/**
|
||||
* Deferred composer: the parent owns the draft value and triggers the post
|
||||
* elsewhere (e.g. the "Send to lab" button). No send icon is shown.
|
||||
*/
|
||||
deferSubmit?: boolean;
|
||||
composerValue?: string;
|
||||
onComposerValueChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
export function LabCaseCommentsPanel({
|
||||
@@ -25,6 +31,9 @@ export function LabCaseCommentsPanel({
|
||||
onPost,
|
||||
onToggleVisibility,
|
||||
onError,
|
||||
deferSubmit = false,
|
||||
composerValue,
|
||||
onComposerValueChange,
|
||||
}: LabCaseCommentsPanelProps) {
|
||||
const t = useTranslations('caseComments');
|
||||
const [comments, setComments] = useState<LabCaseComment[]>([]);
|
||||
@@ -51,7 +60,7 @@ export function LabCaseCommentsPanel({
|
||||
|
||||
async function handlePost() {
|
||||
const trimmed = body.trim();
|
||||
if (!trimmed || !canPost) return;
|
||||
if (!trimmed || !canPost || posting) return;
|
||||
setPosting(true);
|
||||
try {
|
||||
const created = await onPost(trimmed, visibleToClinic);
|
||||
@@ -65,6 +74,13 @@ export function LabCaseCommentsPanel({
|
||||
}
|
||||
}
|
||||
|
||||
function handleComposerKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
void handlePost();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToggle(comment: LabCaseComment) {
|
||||
if (!onToggleVisibility || !canToggleVisibility) return;
|
||||
try {
|
||||
@@ -112,12 +128,8 @@ export function LabCaseCommentsPanel({
|
||||
type="button"
|
||||
onClick={() => void handleToggle(comment)}
|
||||
className="shrink-0 p-1 rounded hover:bg-border text-text-muted"
|
||||
title={
|
||||
comment.visibleToClinic ? t('makeHidden') : t('makeVisible')
|
||||
}
|
||||
aria-label={
|
||||
comment.visibleToClinic ? t('makeHidden') : t('makeVisible')
|
||||
}
|
||||
title={comment.visibleToClinic ? t('makeHidden') : t('makeVisible')}
|
||||
aria-label={comment.visibleToClinic ? t('makeHidden') : t('makeVisible')}
|
||||
>
|
||||
{comment.visibleToClinic ? (
|
||||
<Eye className="h-4 w-4" />
|
||||
@@ -132,33 +144,54 @@ export function LabCaseCommentsPanel({
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{canPost ? (
|
||||
<div className="space-y-2 border-t border-border pt-2">
|
||||
{canPost && deferSubmit ? (
|
||||
<div className="border-t border-border pt-2">
|
||||
<textarea
|
||||
value={body}
|
||||
onChange={(e) => setBody(e.target.value)}
|
||||
value={composerValue ?? ''}
|
||||
onChange={(e) => onComposerValueChange?.(e.target.value)}
|
||||
placeholder={t('placeholder')}
|
||||
rows={2}
|
||||
className="w-full rounded-md border border-border bg-surface px-3 py-2 text-sm resize-none"
|
||||
/>
|
||||
{canToggleVisibility ? (
|
||||
<label className="flex items-center gap-2 text-xs text-text-muted cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={visibleToClinic}
|
||||
onChange={(e) => setVisibleToClinic(e.target.checked)}
|
||||
/>
|
||||
{t('visibleToClinicToggle')}
|
||||
</label>
|
||||
) : null}
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
disabled={posting || !body.trim()}
|
||||
onClick={() => void handlePost()}
|
||||
>
|
||||
{t('post')}
|
||||
</Button>
|
||||
</div>
|
||||
) : canPost ? (
|
||||
<div className="flex items-end gap-2 border-t border-border pt-2">
|
||||
<textarea
|
||||
value={body}
|
||||
onChange={(e) => setBody(e.target.value)}
|
||||
onKeyDown={handleComposerKeyDown}
|
||||
placeholder={t('placeholder')}
|
||||
rows={2}
|
||||
className="min-w-0 flex-1 rounded-md border border-border bg-surface px-3 py-2 text-sm resize-none"
|
||||
/>
|
||||
<div className="flex items-center gap-1 pb-1">
|
||||
{canToggleVisibility ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setVisibleToClinic((v) => !v)}
|
||||
className={`shrink-0 p-2 rounded-md border transition-colors ${
|
||||
visibleToClinic
|
||||
? 'border-primary/40 bg-primary/10 text-primary'
|
||||
: 'border-border text-text-muted hover:text-text-primary'
|
||||
}`}
|
||||
title={visibleToClinic ? t('composerVisible') : t('composerHidden')}
|
||||
aria-label={visibleToClinic ? t('composerVisible') : t('composerHidden')}
|
||||
aria-pressed={visibleToClinic}
|
||||
>
|
||||
{visibleToClinic ? <Eye className="h-4 w-4" /> : <EyeOff className="h-4 w-4" />}
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void handlePost()}
|
||||
disabled={posting || !body.trim()}
|
||||
className="shrink-0 p-2 rounded-md bg-primary text-white transition-colors hover:opacity-90 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
title={t('send')}
|
||||
aria-label={t('send')}
|
||||
>
|
||||
<Send className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user