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:
riyadhafraa
2026-05-05 12:58:23 +00:00
parent cec99c549f
commit 7c2e4bc8f6
5 changed files with 65 additions and 16 deletions
+6
View File
@@ -48,6 +48,12 @@ uri = "file://artifacts/tx-os/public/service-images/black-coffee.png"
type = "image"
title = "black coffee mug"
[[generated]]
id = "pFSmazHx0m3gsXrCE0oR2"
uri = "file://artifacts/tx-os/public/service-images/saudi-coffee.png"
type = "image"
title = "saudi coffee dallah"
[[outputs]]
id = "LYgvZ2mSFO2a8Q7YzWuYh"
uri = "file://attached_assets/tx-icon/main.svg"
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
+2
View File
@@ -110,6 +110,8 @@
"orderNotes": "ملاحظات (اختياري)",
"orderNotesPlaceholder": "أي ملاحظات إضافية؟",
"orderNotesCounter": "{{count}}/500",
"addNotes": "إضافة ملاحظات",
"hideNotes": "إخفاء",
"placeOrder": "إرسال",
"placing": "جارٍ الإرسال...",
"orderPlaced": "تم إرسال طلبك",
+2
View File
@@ -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",