Polish Services: Saudi Coffee rename + image, fix Black Coffee Arabic, hide order notes behind a button (Task #394)
Three small UX fixes on the Services screen: 1. Renamed service id=2 to "قهوة سعودي" / "Saudi Coffee" and swapped the thumbnail to a new clear brown image (small dallah pouring into a finjan with cardamom + a date) saved at public/service-images/saudi-coffee.png. The legacy arabic-coffee.png is left in place so any cached references keep loading. 2. Renamed service id=2450 Arabic name to "بلاك كوفي" (English "Black Coffee" unchanged). 3. OrderServiceModal now opens with the notes textarea collapsed and not mounted, so iPad/Safari no longer auto-pops the keyboard. A small outline "إضافة ملاحظات / Add notes" button (with pencil icon) expands it and focuses the textarea on demand. While expanded, a small "إخفاء / Hide" link collapses it again without clearing typed text. If initialNotes is non-empty when the dialog opens, it starts expanded. Submission behavior unchanged. Verified DB state after updates: id=2 name_ar=قهوة سعودي name_en=Saudi Coffee image_url=service-images/saudi-coffee.png id=2450 name_ar=بلاك كوفي name_en=Black Coffee image_url=service-images/black-coffee.png Added i18n keys services.addNotes / services.hideNotes to ar.json and en.json. Added data-testid="order-notes-toggle" and data-testid="order-notes-textarea" for future tests. `pnpm --filter @workspace/tx-os typecheck` passes. The pre-existing `test` workflow failure is unrelated to this change.
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { ImageIcon } from "lucide-react";
|
||||
import { ImageIcon, Pencil } from "lucide-react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
|
||||
const NOTES_MAX = 500;
|
||||
@@ -46,16 +46,29 @@ export function OrderServiceModal({
|
||||
const { toast } = useToast();
|
||||
const [notes, setNotes] = useState("");
|
||||
const [imgErrored, setImgErrored] = useState(false);
|
||||
const [showNotes, setShowNotes] = useState(false);
|
||||
const notesRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const createOrder = useCreateServiceOrder();
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setNotes((initialNotes ?? "").slice(0, NOTES_MAX));
|
||||
const initial = (initialNotes ?? "").slice(0, NOTES_MAX);
|
||||
setNotes(initial);
|
||||
setImgErrored(false);
|
||||
setShowNotes(initial.trim().length > 0);
|
||||
}
|
||||
}, [open, service?.id, initialNotes]);
|
||||
|
||||
const handleToggleNotes = () => {
|
||||
setShowNotes(true);
|
||||
requestAnimationFrame(() => {
|
||||
notesRef.current?.focus();
|
||||
const len = notesRef.current?.value.length ?? 0;
|
||||
notesRef.current?.setSelectionRange(len, len);
|
||||
});
|
||||
};
|
||||
|
||||
if (!service) return null;
|
||||
const name = lang === "ar" ? service.nameAr : service.nameEn;
|
||||
const img = resolveImage(service.imageUrl);
|
||||
@@ -115,20 +128,46 @@ export function OrderServiceModal({
|
||||
<div className="text-base font-medium text-foreground">{name}</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="order-notes">{t("services.orderNotes")}</Label>
|
||||
<Textarea
|
||||
id="order-notes"
|
||||
value={notes}
|
||||
onChange={(e) => setNotes(e.target.value.slice(0, NOTES_MAX))}
|
||||
placeholder={t("services.orderNotesPlaceholder")}
|
||||
maxLength={NOTES_MAX}
|
||||
rows={3}
|
||||
/>
|
||||
<div className="text-[11px] text-muted-foreground text-end">
|
||||
{t("services.orderNotesCounter", { count: notes.length })}
|
||||
{showNotes ? (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="order-notes">{t("services.orderNotes")}</Label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowNotes(false)}
|
||||
className="text-[11px] text-muted-foreground hover:text-foreground underline-offset-2 hover:underline"
|
||||
data-testid="order-notes-hide"
|
||||
>
|
||||
{t("services.hideNotes")}
|
||||
</button>
|
||||
</div>
|
||||
<Textarea
|
||||
id="order-notes"
|
||||
ref={notesRef}
|
||||
value={notes}
|
||||
onChange={(e) => setNotes(e.target.value.slice(0, NOTES_MAX))}
|
||||
placeholder={t("services.orderNotesPlaceholder")}
|
||||
maxLength={NOTES_MAX}
|
||||
rows={3}
|
||||
data-testid="order-notes-textarea"
|
||||
/>
|
||||
<div className="text-[11px] text-muted-foreground text-end">
|
||||
{t("services.orderNotesCounter", { count: notes.length })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleToggleNotes}
|
||||
className="self-start gap-2"
|
||||
data-testid="order-notes-toggle"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
{t("services.addNotes")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<DialogFooter className="gap-2 sm:gap-2">
|
||||
<Button
|
||||
|
||||
@@ -110,6 +110,8 @@
|
||||
"orderNotes": "ملاحظات (اختياري)",
|
||||
"orderNotesPlaceholder": "أي ملاحظات إضافية؟",
|
||||
"orderNotesCounter": "{{count}}/500",
|
||||
"addNotes": "إضافة ملاحظات",
|
||||
"hideNotes": "إخفاء",
|
||||
"placeOrder": "إرسال",
|
||||
"placing": "جارٍ الإرسال...",
|
||||
"orderPlaced": "تم إرسال طلبك",
|
||||
|
||||
@@ -110,6 +110,8 @@
|
||||
"orderNotes": "Notes (optional)",
|
||||
"orderNotesPlaceholder": "Any extra notes?",
|
||||
"orderNotesCounter": "{{count}}/500",
|
||||
"addNotes": "Add notes",
|
||||
"hideNotes": "Hide",
|
||||
"placeOrder": "Send",
|
||||
"placing": "Sending...",
|
||||
"orderPlaced": "Order placed",
|
||||
|
||||
Reference in New Issue
Block a user