برای ایجاد یه تجربه کاربری بهتر زمان استفاده از اپلیکیشن ثبت تراکنش های مالی کوئرا، قراره ساختاری رو پیاده سازی کنیم تا کاربر در هر دو حالت آنلاین و آفلاین امکان استفاده از اپلیکیشن رو داشته باشه و حتی در صورت آفلاین بودن، اختلالی در روند کار به وجود نیاد.
ظاهر کلی برنامه به شکل زیر است:
![خروجی](https://quera.org/qbox/view/9yMVPGlJxx/E.gif)
# پروژه اولیه
پروژه اولیه را از
[این لینک](/problemset/assignments/4367/download_problem_initial_project/254942/)
دانلود کنید. ساختار فایلهای این پروژه به صورت زیر است.
```
Finance
├── public
│ ├── index.html
│ └── service-worker.js
├── src
│ ├── assets
│ │ └── logo.svg
│ ├── components
│ │ ├── TransactionForm.js
│ │ └── TransactionsList.js
│ ├── App.js
│ ├── api.js
│ ├── index.js
│ ├── indexedDB.js
│ ├── serviceWorkerRegistration.js
├── db.json
```
<details class="green">
<summary>
**راهاندازی پروژه**
</summary>
فایل دانلود شده را از حالت فشرده خارج و دستور `npm install` را در `root` پروژه اجرا کنید.
همچنین برای هاست پروژه به صورت لوکال، میتوانید از دستور `npm start` استفاده کنید.
برای راه اندازی سرویس json server، میتوانید از دستور `npm run db`استفاده کنید.
</details>
### حالت آنلاین
نحوه عملکرد این برنامه به این شکل خواهد بود که در ابتدا دادهها (تراکنش های مالی) از سمت یک سرویس دریافت خواهد شد (تابع `getServerTransactions`). علاوه بر نمایش دادههای دریافت شده، این اطلاعات باید در حافظه کش مرورگر (با استفاده از service workers) ذخیره شود.
بعد از ثبت هر تراکنش در حالت آنلاین (ارسال به `json server` با تابع `sendTransactionToServer`)، باید دادههای داخل حافظه `cache` هم به بروز شوند و آخرین تراکنش ثبت شده به لیست اطلاعات قبلی داخل `cache` اضافه شود.
### حالت آفلاین
همچنین در حالت آفلاین باید همچنان دادهها قابل نمایش باشند و در این حالت تراکنش جدید باید به `store` تعریف شده با نام (`transactions`) در `indexedDB` اضافه شود و همچنین نیازی به بروز شدن حافظه `cache` نخواهد بود. دقت شود که در حالت آفلاین باید اطلاعات ذخیره شده در `cache` همراه با دادههای `indexedDB` همزمان نمایش داده شوند.
در نهایت بعد از اتصال مجدد شبکه و دسترسی به اینترنت باید داده های `indexedDB` بررسی شود و در صورتی که تراکنشی تکراری وجود نداشت عملیات `sync` آغاز شود و اطلاعات ذخیره شده در مروگر به سمت سرور (`json server`) ارسال و ذخیره شود و همچنین اطلاعات داخل `indexedDB` به طور کامل حذف شوند.
### کامپوننت TransactionForm
- این کامپوننت باید به شکلی تکمیل شود که اطلاعات وارد شده از `input` ها دریافت و ذخیره شود و در نهایت در صورت کلیک روی دکمه `Add` توسط تابع مشخص شده به کامپوننت `TransactionsList` ارسال شود.
### کامپوننت TransactionsList
- تابع **handleAddTransaction** برای ثبت یک تراکنش جدید استفاده خواهد شد، به شکلی که در صورت **آنلاین** بودن یک درخواست سمت سرور ارسال میشود و همچنین بعد از موفق بودن درخواست مقدار داده های `cache` شده مجدد با تراکنش جدید بروز خواهد شد. و در صورت **آفلاین** بودن اپلیکیشن مقدار تراکنش جدید باید به صورت یک ردیف جدید توسط تابع `saveDBTransaction` در یک `store` با نام `transactions` در `indexedDB` ذخیره شود.
- تابع **loadTransactions** وظیفه دریافت اطلاعات جهت نمایش را خواهد داشت که در صورت آنلاین بودن داده از تابع `getServerTransactions` دریافت خواهند شد و همچنین در زمان آفلاین بود با استفاده از تابع `getDBTransactions` اطلاعات جهت نمایش دریافت میشود (در صورت نیاز امکان دریافت همزمان از هر دو تابع خواهد بود).
- تابع **syncTransactions** وظیفه `sync` کردن اطلاعات ذخیره شده در `indexedDB` با اطلاعات سمت سرور (`json server`) را خواهد داشت، به شکلی که باید جلوی ثبت اطلاعات تکراری گرفته شود. همچنین بعد از `sync` شدن، اطلاعات باید اطلاعات داخل `indexedDB` حذف شوند.
### فایل api
- تابع **getServerTransactions** وظیفه دریافت اطلاعات از سمت سرور (`json server`) را خواهد داشت، در صورتی که نتیجه موفقیتآمیز بود مقدار داده های دریافتی برگشت داده خواهد شد و در صورت بروز خطا، باید خطایی با عنوان "Failed to fetch" برگشت داده شود.
- تابع **sendTransactionToServer** وظیفه ذخیره یک تراکنش جدید در سرور را خواهد داشت، در صورتی که نتیجه موفقیتآمیز بود، مقدار داده ذخیره شده برگشت داده خواهد شد و در صورت بروز خطا، باید خطایی با عنوان `"Failed to save"` برگشت داده شود.
### فایل indexedDB
- تابع **saveDBTransaction** از نوع `Promise` وظیفه ذخیره اطلاعات تراکنش را در `indexedDB` خواهد داشت که در صورت بروز خطا در ثبت باید مقدار `"Failed to save"` و در صورت موفقیت آمیز بودن مقدار `true` برگشت داده شود.
- تابع **getDBTransactions** از نوع `Promise` وظیفه دریافت اطلاعات از `indexedDB` را خواهد داشت که در صورت بروز خطا در دریافت اطلاعات باید مقدار `"Failed to fetch"` و در صورت موفقیت آمیز بودن داده های دریافتی برگشت داده شود.
- تابع **deleteDBTransaction** از نوع `Promise` وظیفه حذف اطلاعات تراکنش از `indexedDB` را دارد که در صورت بروز خطا در حذف تراکنش باید مقدار `"Failed to delete"` و در صورت موفقیت آمیز بودن مقدار `true` برگشت داده شود.
[راهنمایی بیشتر indexedDB](https://javascript.info/indexeddb#transactions)
### فایل serviceWorkerRegistration
- قبل از شروع فرآیند تکمیل پروژه بهتر است دو تابع `register` و `addItemToCache` تکمیل شوند که به ترتیب وظایف ثبت کردن یک service worker و اضافه کردن تراکنش جدید به حافظه `cache` مروگر را خواهند داشت.
# جزئیات
- قبل از شروع فرآیند حل سوال بررسی فایل `service-worker.js` برای درک بهتر نحوه کار نرمافزار توصیه میشود.
- دقت شود که زمان ثبت یک تراکنش جدید، باید از ثبت داده های تکراری (تراکنش تکراری از نظر قیمت و توضیحات) جلوگیری شود.
- مقدار تراکنش ذخیره شده باید شامل سه کلید `amount, description, id` باشد.
# نکات
+ شما مجاز به نصب کتابخانههای خارجی از جمله `npm` نیستید.
+ شما مجاز به اعمال تغییرات در
`TransactionForm.jsx`، `TransactionsList.jsx`، `api.js`، `indexedDB.js` و `serviceWorkerRegistration.js` هستید.
+ میتوانید فایل های خواسته شده را بعد از اعمال تغییرات به صورت *ZIP* برای ما ارسال کنید.