feat: add Vazir font and refactor form components

- Add vazir-font package for Persian/Arabic text support
- Fix dropdown search to filter by value instead of name
- Refactor select field in FormField component with simplified options mapping
- Comment out dependent field fetching logic (WIP)
- Update BaseDropdown props structure
This commit is contained in:
MehrdadAdabi 2025-11-28 13:55:10 +03:30
parent 0cd6d7fd3f
commit 42f9ffec95
5 changed files with 93 additions and 36 deletions

8
package-lock.json generated
View File

@ -24,6 +24,7 @@
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"vazir-font": "^30.1.0",
"vite-plugin-pwa": "^1.1.0" "vite-plugin-pwa": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
@ -7819,6 +7820,13 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
} }
}, },
"node_modules/vazir-font": {
"version": "30.1.0",
"resolved": "https://registry.npmjs.org/vazir-font/-/vazir-font-30.1.0.tgz",
"integrity": "sha512-XN2Uprw/Q3QhAAApITykf+v0l4p4FpqvIh0h2XDSJRjwUiYeDpwQxECQF/93ZOCDO2t8haBi6BZZqO8sifYNRg==",
"deprecated": "vazir-font no longer supported. Use vazirmatn instead.",
"license": "OFL"
},
"node_modules/vite": { "node_modules/vite": {
"version": "7.2.4", "version": "7.2.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",

View File

@ -27,6 +27,7 @@
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"vazir-font": "^30.1.0",
"vite-plugin-pwa": "^1.1.0" "vite-plugin-pwa": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -125,7 +125,7 @@ export const BaseDropdown = forwardRef<HTMLDivElement, BaseDropdownProps>(
}; };
const filteredOptions = internalOptions.filter((option) => const filteredOptions = internalOptions.filter((option) =>
option.name.toLowerCase().includes(searchQuery.toLowerCase()) option.value.toLowerCase().includes(searchQuery.toLowerCase())
); );
const hasError = !!error; const hasError = !!error;

View File

@ -2,9 +2,8 @@ import type { FieldDefinition } from "@/core/utils/dynamic-field.utils";
import { import {
fieldTypeMap, fieldTypeMap,
FieldTypes, FieldTypes,
FormService,
normalizeInput, normalizeInput,
regexValidators, regexValidators
} from "@/core/utils/dynamic-field.utils"; } from "@/core/utils/dynamic-field.utils";
import { useEffect, useState, type ChangeEvent, type FC } from "react"; import { useEffect, useState, type ChangeEvent, type FC } from "react";
import { BaseDropdown } from "./base-drop-down"; import { BaseDropdown } from "./base-drop-down";
@ -254,31 +253,30 @@ const FormField: FC<FormFieldProps> = ({
); );
case "select": case "select":
{ {
const isDependent = field.Parent_FieldID !== undefined; const isDependent = field.Parent_FieldID !== 0;
const parentFieldValue = // const parentFieldValue =
isDependent && field.Parent_FieldID !== undefined // isDependent && field.Parent_FieldID !== undefined
? allFormValues[field.Parent_FieldID.toString()] // ? allFormValues[field.Parent_FieldID.toString()]
: undefined; // : undefined;
// debugger
const fetchDependentOptions = isDependent // const fetchDependentOptions = isDependent
? async (inputValue: string) => { // ? async (inputValue: string) => {
if (!parentFieldValue || field.Parent_FieldID === undefined) // debugger
return []; // Don't fetch if parent value is not set or Parent_FieldID is undefined // if (!parentFieldValue || field.Parent_FieldID === undefined)
const fetched = await FormService.fetchDependentOptions( // return []; // Don't fetch if parent value is not set or Parent_FieldID is undefined
field.Parent_FieldID!, // Non-null assertion // const fetched = await FormService.fetchDependentOptions(
parentFieldValue // field.Parent_FieldID!, // Non-null assertion
); // parentFieldValue
return fetched.filter((option) => // );
option.label.toLowerCase().includes(inputValue.toLowerCase()) // return fetched.filter((option) =>
); // option.label.toLowerCase().includes(inputValue.toLowerCase())
} // );
: undefined; // }
// : undefined;
return ( return (
<BaseDropdown <BaseDropdown
label={field.Name} options={field.parsedOptions?.map(el => ({ name: el.value, value: el.value }))}
error={error || undefined}
options={field.parsedOptions || []}
value={ value={
field.parsedOptions?.find((opt) => opt.value === value)?.value || field.parsedOptions?.find((opt) => opt.value === value)?.value ||
undefined undefined
@ -286,8 +284,7 @@ const FormField: FC<FormFieldProps> = ({
onChange={(optionValue: string) => onChange(optionValue)} onChange={(optionValue: string) => onChange(optionValue)}
className={`w-full p-2 border rounded-md text-right ${error ? "border-red-500" : "border-gray-300" className={`w-full p-2 border rounded-md text-right ${error ? "border-red-500" : "border-gray-300"
}`} }`}
fetchOptions={fetchDependentOptions} disabled={isDependent} // Disable if dependent and parent value is not set
disabled={isDependent && !parentFieldValue} // Disable if dependent and parent value is not set
/> />
); );
} }

View File

@ -5,19 +5,69 @@
/* Persian Font Support */ /* Persian Font Support */
@font-face { @font-face {
font-family: "Vazir"; font-family: Vazir;
src: url("https://cdn.jsdelivr.net/npm/vazir-font@31.1.0/dist/Vazir.woff2") src: url('Vazir-Regular.eot');
format("woff2"); src: url('Vazir-Regular.eot?#iefix') format('embedded-opentype'),
url('Vazir-Regular.woff2') format('woff2'),
url('Vazir-Regular.woff') format('woff'),
url('Vazir-Regular.ttf') format('truetype');
font-weight: normal; font-weight: normal;
font-display: swap; font-style: normal;
} }
@font-face { @font-face {
font-family: "Vazir"; font-family: Vazir;
src: url("https://cdn.jsdelivr.net/npm/vazir-font@31.1.0/dist/Vazir-Bold.woff2") src: url('Vazir-Bold.eot');
format("woff2"); src: url('Vazir-Bold.eot?#iefix') format('embedded-opentype'),
url('Vazir-Bold.woff2') format('woff2'),
url('Vazir-Bold.woff') format('woff'),
url('Vazir-Bold.ttf') format('truetype');
font-weight: bold; font-weight: bold;
font-display: swap; font-style: normal;
}
@font-face {
font-family: Vazir;
src: url('Vazir-Black.eot');
src: url('Vazir-Black.eot?#iefix') format('embedded-opentype'),
url('Vazir-Black.woff2') format('woff2'),
url('Vazir-Black.woff') format('woff'),
url('Vazir-Black.ttf') format('truetype');
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: Vazir;
src: url('Vazir-Medium.eot');
src: url('Vazir-Medium.eot?#iefix') format('embedded-opentype'),
url('Vazir-Medium.woff2') format('woff2'),
url('Vazir-Medium.woff') format('woff'),
url('Vazir-Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: Vazir;
src: url('Vazir-Light.eot');
src: url('Vazir-Light.eot?#iefix') format('embedded-opentype'),
url('Vazir-Light.woff2') format('woff2'),
url('Vazir-Light.woff') format('woff'),
url('Vazir-Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: Vazir;
src: url('Vazir-Thin.eot');
src: url('Vazir-Thin.eot?#iefix') format('embedded-opentype'),
url('Vazir-Thin.woff2') format('woff2'),
url('Vazir-Thin.woff') format('woff'),
url('Vazir-Thin.ttf') format('truetype');
font-weight: 100;
font-style: normal;
} }
:root { :root {
@ -137,6 +187,7 @@
* { * {
@apply border-border outline-ring/50; @apply border-border outline-ring/50;
} }
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
margin: 0 auto; margin: 0 auto;