# پروژه اولیه
پروژهی اولیه را از
[این لینک](/problemset/assignments/4367/download_problem_initial_project/132257/)
دانلود کنید.
ساختار فایلهای این پروژه به صورت زیر است.
<details class="green">
<summary>
ساختار پروژه
</summary>
```
pindo
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ └── logo512.png
├── src
│ ├── assets
│ │ └── font
│ │ └── Vazir.ttf
│ ├── components
│ │ ├── Button
│ │ │ ├── Button.css
│ │ │ └── index.jsx
│ │ ├── Checkbox
│ │ │ └── index.jsx
│ │ ├── CitySelector
│ │ │ ├── CitySelectorItem.jsx
│ │ │ └── index.jsx
│ │ ├── Dropdown
│ │ │ └── index.jsx
│ │ ├── Error
│ │ │ └── index.jsx
│ │ ├── TextInput
│ │ │ └── index.jsx
│ │ ├── Textarea
│ │ │ └── index.jsx
│ │ └── FormItem.jsx
│ ├── constants
│ │ ├── Errors.js
│ │ └── FormType.js
│ ├── data
│ │ ├── cities.js
│ │ └── formData.js
│ ├── App.css
│ ├── App.jsx
│ ├── index.css
│ └── index.js
└── package.json
```
</details>
<details class="brown">
<summary>
راهاندازی پروژه
</summary>
**برای اجرای پروژه، باید `NodeJS` و `npm` را از قبل نصب کرده باشید.**
- ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
- در پوشهی `pindo` ، دستور `npm install` را برای نصب نیازمندیها اجرا کنید.
- در همین پوشه، دستور `npm start` را برای راهاندازی پروژه اجرا کنید.
- پس از انجام موفق این مراحل، با مراجعه به آدرس `http://localhost:3000/`
میتوانید نتیجه را ببینید.
</details>
# جزئیات
ظاهر کلی برنامه بهاین صورت است:
![ظاهر برنامه](https://quera.ir/qbox/view/d0jAsU8SZt/pindo.gif)
یکی از قسمتهای اصلی پروژه پیندو ایجاد آگهی است. کاربران در دستهبندیهای مختلفی میتوانند برای کالای خود آگهی ایجاد کنند.
برای مثال فرم ایجاد آگهی برای گوشی موبایل با ظروف آشپزخانه متفاوت است.
به همین جهت فرم ایجاد آگهی باید به صورت داینامیک باشد.
در این سوال از شما میخواهیم این فرمهای داینامیک را بسازید.
اِلِمانهای فرم در یک آرایه قرار دارد.
ساختار این آرایه به شکل زیر است:
```json
{
"type": string,
"required": boolean | undefined,
"name": string,
"label": string,
"options": { "title": string, "value": number }[],
"dependency": {
"show": { "name": string, "value": string | boolean | number },
"required": { "name": string, "value": string | boolean | number },
} | undefined,
}
```
#### فیلد *type*
این فیلد نشاندهندهی نوع اِلمان است.
هر المان فرم میتواند یکی از مقادیر *type* زیر را داشته باشد:
```js
const FormType = {
TEXT: "text",
DROPDOWN: "dropdown",
TEXTAREA: "textarea",
CHECKBOX: "checkbox",
CITY: "city",
};
```
#### فیلد *required*
این فیلد نشان میدهد که آیا تکمیل این المان اجباری خواهد بود یا خیر.
درصورتی که این فیلد `true` باشد، کاربر باید حتما برای این فیلد مقداری را وارد کند.
این فیلد میتواند خالی باشد و این به معنای غیراجباری بودن این فیلد است.
#### فیلد *name*
این فیلد نشاندهندهی کلید اصلی آن المان است. در هنگام ساخت دادههای ورودی کاربر، مقدار کلید برابر این فیلد است.
#### فیلد *label*
این فیلد مشخصکنندهی برچسب (لیبل) المان است.
#### فیلد *options*
در صورتی که *type* یک المان برابر *FormType.DROPDOWN* باشد، مقادیری قابل انتخاب در فرم *select* در این
فیلد قرار میگیرد.
#### فیلد *dependency*
هر المان میتواند به المان دیگری وابستگی داشته باشد. این موضوع در این فیلد مشخص میشود.
در صورتی که این فیلد وجود نداشت، به این معنا است که این فیلد به فیلد دیگری وابستگی ندارد.
وابستگی به دو شکل وجود دارد:
- وابستگی *show*: به این معنا است که نمایش این فیلد به مقدار یک فیلد دیگر وابسته است.
- وابستگی *required*: به این معنا است که اجباری بودن این فیلد به مقدار یک فیلد دیگر وابسته است.
هر کدام از این وابستگیها به شکل زیر مشخص میشوند:
```json
{ "name": string, "value": string | boolean | number }
```
که *name* نشاندهندهی فیلدی است که به آن وابسته است و *value* نشاندهندهی شرط وابستگی است.
در صورتی که مقدار فیلد *name* برابر *value* بود، شرط وابستگی برقرار است.
برای مثال در دادهی زیر فیلد *productIssues* به فیلد *productStatus* وابسته است.
در صورتی که مقدار انتخابی در *productStatus* برابر **4** باشد (یعنی مقدار انتخابی **خراب** باشد)،
این فیلد باید (*productIssues*) نمایش داده شود.
همچنین اجباریبودن فیلد *city* به مقدار فیلد *delivery* بستگی دارد و مقدار این فیلد باید `true` باشد.
دقت کنید این فیلد باید نمایش داده شود ولی اجباریبودن آن به فیلد دیگری بستگی دارد.
```json
[
{
"type": "dropdown",
"name": "productStatus",
"label": "وضعیت کالا",
"options": [
{ "title": "نو", "value": 1 },
{ "title": "در حد نو", "value": 2 },
{ "title": "دست دوم", "value": 3 },
{ "title": "خراب", "value": 4 },
],
},
{
"type": "textarea",
"name": "productIssues",
"label": "مشکلات کالا",
"dependency": {
"show": { "name": "productStatus", "value": 4 },
},
},
{
"type": "checkbox",
"name": "delivery",
"label": "از سیستم دلیوری استفاده خواهم کرد",
},
{
"type": "city",
"name": "cityId",
"label": "محل آگهی",
"dependency": {
"required": { "name": "delivery", "value": true },
},
},
];
```
#### مواردی که باید در این سوال رعایت کنید:
- شما باید برنامهای بنویسید که لیست فیلدها را دریافت کند و به این شکل ساختاری نمایش دهد.
- فرم نهایی در صورتی باید *submit* شود که تمام شرایط لازم در آن رعایت شده باشد (تمام فیلدهای اجباری وارد شده باشند).
- مقادیر وارد شده باید به تابع `onSubmit` پاس داده شود. (این تابع به شکل *prop* به کامپوننت داده میشود)
- در صورتی که یک فیلد اجباری پر نشده بود، باید برای آن فیلد خطا نمایش داده شود (کاپوننت آن آورده شده است و فقط لازم است برحسب شرایط از آن استفاده کنید).
- در صورتی که یک فیلد غیراجباری پر نشده بود، باید برای آن فیلد مقدار `undefiend` به تابع `onSubmit`
فرستاده شود.
#### تغییرات لازم برای هر فایل:
- فایل `App.jsx`: منطق اصلی کد شما در این فایل قرار میگیرد.
در این فایل شما *props* را بهعنوان ورودی میگیرید.
- *onSubmit*: زمانی که فرم بدون خطا بود، به این تابع مقادیر وارد شده را بهعنوان پارامتر ورودی بفرستید.
- *fields*: فیلدهای فرم در این *prop* قرار میگیرد.
- فایل `FormItem.jsx`: در این کامپوننت شما براساس *type* هر المان باید از یکی کامپوننتهای آورده شده،
استفاده کنید.
شما لازم است براساس هر آیتم در *fields* از این کامپوننت استفاده کنید.
# نکات
- حتما از کامپوننتهای داخل پروژه استفاده شود، در غیر این صورت نمره سوال را نخواهید گرفت.
- شما تنها مجاز به اعمال تغییرات در فایلهای `App.jsx` و `FormItem.jsx` و `package.json` هستید.
- به کامنتهای داخل فایلها دقت کنید.
- استفاده از کتابخانههای خارجی مجاز است. (در پروژه اولیه کتابخانهی
[react-hook-form](https://react-hook-form.com/get-started#IntegratingwithUIlibraries)
قرار داده شده است)
- برای ارسال پاسخ کل پروژه را *zip* کرده و ارسال کنید. دقت کنید که پوشهی *node_modules* در فایل ارسالی نباشد.