humam kerdiah 4bfd8c84a9 feat: add template checkpoint edit dialog and vendor management components
- Implemented TemplateCheckpointEditDialog for creating and editing inspection checkpoints.
- Added VendorActions component for managing vendor actions including edit, activate/deactivate, and delete.
- Created VendorContext for managing vendor state across components.
- Developed VendorGeneralInfo component to display detailed vendor information.
- Introduced AedSymbol and Money components for consistent currency representation.
- Added PromptDialog for user input prompts throughout the application.
- Implemented RelationLink component for unified related-data display in CRUD tables.
- Created InspectionTemplatesClient for API interactions related to inspection templates.
2026-05-18 12:08:42 +04:00

122 lines
3.5 KiB
TypeScript

"use client"
import { useEffect, useState } from "react"
import { create } from "zustand"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/shared/components/ui/dialog"
import { Button } from "@/shared/components/ui/button"
import { Input } from "@/shared/components/ui/input"
import { Label } from "@/shared/components/ui/label"
// ── Types ──
export type PromptOptions = {
title?: string
description?: string
label?: string
placeholder?: string
defaultValue?: string
confirmLabel?: string
cancelLabel?: string
required?: boolean
}
type PromptStore = {
open: boolean
options: PromptOptions
resolve: ((value: string | null) => void) | null
_show: (options: PromptOptions) => Promise<string | null>
_close: (value: string | null) => void
}
// ── Store ──
const usePromptStore = create<PromptStore>((set, get) => ({
open: false,
options: {},
resolve: null,
_show: (options) =>
new Promise<string | null>((resolve) => {
set({ open: true, options, resolve })
}),
_close: (value) => {
const { resolve } = get()
resolve?.(value)
set({ open: false, resolve: null })
},
}))
// ── Imperative API ──
export function prompt(options: PromptOptions = {}): Promise<string | null> {
return usePromptStore.getState()._show(options)
}
// ── Dialog component (mount once in root layout) ──
export function PromptDialog() {
const { open, options, _close } = usePromptStore()
const [value, setValue] = useState("")
// Reset value whenever the dialog opens
useEffect(() => {
if (open) setValue(options.defaultValue ?? "")
}, [open, options.defaultValue])
const submit = () => {
if (options.required !== false && value.trim() === "") return
_close(value.trim())
}
return (
<Dialog
open={open}
onOpenChange={(v) => {
if (!v) _close(null)
}}
>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{options.title ?? "Enter a value"}</DialogTitle>
{options.description && (
<DialogDescription>{options.description}</DialogDescription>
)}
</DialogHeader>
<div className="grid gap-2">
{options.label && <Label htmlFor="prompt-input">{options.label}</Label>}
<Input
id="prompt-input"
autoFocus
value={value}
placeholder={options.placeholder}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault()
submit()
}
}}
/>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => _close(null)}>
{options.cancelLabel ?? "Cancel"}
</Button>
<Button onClick={submit} disabled={options.required !== false && value.trim() === ""}>
{options.confirmLabel ?? "OK"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}