From 45e62090435bd86e34547de4041699d5410cd2ce Mon Sep 17 00:00:00 2001 From: Mateusz Szymanski Date: Sun, 26 Apr 2026 04:03:23 +0200 Subject: [PATCH 1/2] feat(nuc_entities): initialize entity management module --- modules/index.ts | 4 + modules/nuc_entities/README.md | 11 ++ .../atomic/bosons/constants/fields/article.ts | 34 +++++ .../atomic/bosons/constants/fields/contact.ts | 42 ++++++ .../atomic/bosons/constants/fields/index.ts | 3 + .../atomic/bosons/constants/fields/money.ts | 39 +++++ .../atomic/bosons/constants/index.ts | 1 + modules/nuc_entities/atomic/bosons/index.ts | 3 + .../atomic/bosons/types/api/Article/index.ts | 1 + .../bosons/types/api/Article/interfaces.ts | 22 +++ .../atomic/bosons/types/api/Contact/index.ts | 1 + .../bosons/types/api/Contact/interfaces.ts | 22 +++ .../atomic/bosons/types/api/Money/index.ts | 1 + .../bosons/types/api/Money/interfaces.ts | 22 +++ .../atomic/bosons/types/api/index.ts | 3 + .../nuc_entities/atomic/bosons/types/index.ts | 2 + .../bosons/types/object/Article/index.ts | 1 + .../bosons/types/object/Article/interfaces.ts | 9 ++ .../bosons/types/object/Contact/index.ts | 1 + .../bosons/types/object/Contact/interfaces.ts | 16 +++ .../atomic/bosons/types/object/Money/index.ts | 1 + .../bosons/types/object/Money/interfaces.ts | 12 ++ .../atomic/bosons/types/object/index.ts | 3 + .../bosons/utils/api/article_requests.ts | 106 ++++++++++++++ .../bosons/utils/api/contact_requests.ts | 106 ++++++++++++++ .../atomic/bosons/utils/api/index.ts | 3 + .../atomic/bosons/utils/api/money_requests.ts | 106 ++++++++++++++ .../nuc_entities/atomic/bosons/utils/index.ts | 1 + modules/nuc_entities/atomic/index.ts | 3 + .../atomic/pages/Article/index.ts | 1 + .../atomic/pages/Article/index.tsx | 25 ++++ .../atomic/pages/Contact/index.ts | 1 + .../atomic/pages/Contact/index.tsx | 25 ++++ .../atomic/pages/General/index.ts | 1 + .../atomic/pages/General/index.tsx | 134 ++++++++++++++++++ .../nuc_entities/atomic/pages/Money/index.ts | 1 + .../nuc_entities/atomic/pages/Money/index.tsx | 25 ++++ modules/nuc_entities/atomic/pages/index.ts | 4 + .../atomic/templates/Dashboard/Article.tsx | 125 ++++++++++++++++ .../atomic/templates/Dashboard/Contact.tsx | 125 ++++++++++++++++ .../atomic/templates/Dashboard/Money.tsx | 124 ++++++++++++++++ .../atomic/templates/Dashboard/index.ts | 3 + .../nuc_entities/atomic/templates/index.ts | 1 + modules/nuc_entities/config.json | 8 ++ modules/nuc_entities/index.ts | 13 ++ modules/nuc_entities/nuc_entities.ts | 19 +++ 46 files changed, 1214 insertions(+) create mode 100644 modules/nuc_entities/README.md create mode 100644 modules/nuc_entities/atomic/bosons/constants/fields/article.ts create mode 100644 modules/nuc_entities/atomic/bosons/constants/fields/contact.ts create mode 100644 modules/nuc_entities/atomic/bosons/constants/fields/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/constants/fields/money.ts create mode 100644 modules/nuc_entities/atomic/bosons/constants/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/Article/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/Article/interfaces.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/Contact/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/Contact/interfaces.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/Money/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/Money/interfaces.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/api/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/Article/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/Article/interfaces.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/Contact/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/Contact/interfaces.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/Money/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/Money/interfaces.ts create mode 100644 modules/nuc_entities/atomic/bosons/types/object/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/utils/api/article_requests.ts create mode 100644 modules/nuc_entities/atomic/bosons/utils/api/contact_requests.ts create mode 100644 modules/nuc_entities/atomic/bosons/utils/api/index.ts create mode 100644 modules/nuc_entities/atomic/bosons/utils/api/money_requests.ts create mode 100644 modules/nuc_entities/atomic/bosons/utils/index.ts create mode 100644 modules/nuc_entities/atomic/index.ts create mode 100644 modules/nuc_entities/atomic/pages/Article/index.ts create mode 100644 modules/nuc_entities/atomic/pages/Article/index.tsx create mode 100644 modules/nuc_entities/atomic/pages/Contact/index.ts create mode 100644 modules/nuc_entities/atomic/pages/Contact/index.tsx create mode 100644 modules/nuc_entities/atomic/pages/General/index.ts create mode 100644 modules/nuc_entities/atomic/pages/General/index.tsx create mode 100644 modules/nuc_entities/atomic/pages/Money/index.ts create mode 100644 modules/nuc_entities/atomic/pages/Money/index.tsx create mode 100644 modules/nuc_entities/atomic/pages/index.ts create mode 100644 modules/nuc_entities/atomic/templates/Dashboard/Article.tsx create mode 100644 modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx create mode 100644 modules/nuc_entities/atomic/templates/Dashboard/Money.tsx create mode 100644 modules/nuc_entities/atomic/templates/Dashboard/index.ts create mode 100644 modules/nuc_entities/atomic/templates/index.ts create mode 100644 modules/nuc_entities/config.json create mode 100644 modules/nuc_entities/index.ts create mode 100644 modules/nuc_entities/nuc_entities.ts diff --git a/modules/index.ts b/modules/index.ts index 5e53680..4b73587 100644 --- a/modules/index.ts +++ b/modules/index.ts @@ -2,6 +2,7 @@ export * from './nuc_api' export * from './nuc_auth' export * from './nuc_charts' export * from './nuc_colors' +export * from './nuc_entities' export * from './nuc_fields' export * from './nuc_globals' export * from './nuc_languages' @@ -14,6 +15,9 @@ export * from './nuc_sections' export * from './nuc_stores' export * from './nuc_templates' export * from './nuc_tooltip' +export { NucEntityDataTableCard } from './nuc_datatable' +export { NucDialog } from './nuc_dialog/index.tsx' +export { useNucDialog } from './nuc_dialog/utils/use_nuc_dialog' export { getEmailUsTextFields } from './nuc_sections/components/email-us/constants/text_fields' export { submitContactForm } from './nuc_sections/components/email-us/utils/submit_form' diff --git a/modules/nuc_entities/README.md b/modules/nuc_entities/README.md new file mode 100644 index 0000000..b43c5b2 --- /dev/null +++ b/modules/nuc_entities/README.md @@ -0,0 +1,11 @@ +#   nuc_entities + +Module for all classic entities in Nucleify. + +
+ +

    Contributors


+ + + + diff --git a/modules/nuc_entities/atomic/bosons/constants/fields/article.ts b/modules/nuc_entities/atomic/bosons/constants/fields/article.ts new file mode 100644 index 0000000..885cea7 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/constants/fields/article.ts @@ -0,0 +1,34 @@ +import type { EntityFieldInterface, UseFieldsInterface } from 'nucleify' + +export function useArticleFields(): UseFieldsInterface { + const fieldData: readonly [string, string, string][] = [ + ['title', 'field-title', 'input-text'], + ['description', 'field-description', 'textarea'], + ['category', 'field-category', 'input-text'], + ['updated_at', 'field-updated-at', ''], + ['created_at', 'field-created-at', ''], + ] as const + + const createAndEditFields: readonly EntityFieldInterface[] = fieldData + .filter(([name]) => !['created_at', 'updated_at'].includes(name)) + .map( + ([name, label, type]): EntityFieldInterface => ({ + name, + label, + type, + }) + ) + + const showFields: readonly { label: string; key: string }[] = fieldData.map( + ([key, label]) => ({ + name: key, + key, + label, + }) + ) + + return { + createAndEditFields, + showFields, + } +} diff --git a/modules/nuc_entities/atomic/bosons/constants/fields/contact.ts b/modules/nuc_entities/atomic/bosons/constants/fields/contact.ts new file mode 100644 index 0000000..332b069 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/constants/fields/contact.ts @@ -0,0 +1,42 @@ +import type { EntityFieldInterface, UseFieldsInterface } from 'nucleify' +import { roles } from 'nucleify' + +export function useContactFields(): UseFieldsInterface { + const fieldData: readonly [string, string, string][] = [ + ['first_name', 'field-first-name', 'input-text'], + ['last_name', 'field-last-name', 'input-text'], + ['email', 'field-email', 'input-text'], + ['personal_phone', 'field-personal-phone', 'input-mask'], + ['work_phone', 'field-work-phone', 'input-mask'], + ['address', 'field-address', 'textarea'], + ['birthday', 'field-birthday', 'date-picker'], + ['contact_groups', 'field-contact-groups', 'input-text'], + ['role', 'field-role', 'select'], + ['updated_at', 'field-updated-at', ''], + ['created_at', 'field-created-at', ''], + ] as const + + const createAndEditFields: EntityFieldInterface[] = fieldData + .filter(([name]) => !['created_at', 'updated_at'].includes(name)) + .map(([name, label, type]): EntityFieldInterface => { + const props = + name === 'role' + ? { options: roles, placeholder: 'field-select-role' } + : undefined + + return { name, label, type, props } + }) + + const showFields: readonly { label: string; key: string }[] = fieldData.map( + ([key, label]) => ({ + name: key, + key, + label, + }) + ) + + return { + createAndEditFields, + showFields, + } +} diff --git a/modules/nuc_entities/atomic/bosons/constants/fields/index.ts b/modules/nuc_entities/atomic/bosons/constants/fields/index.ts new file mode 100644 index 0000000..d7c1d56 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/constants/fields/index.ts @@ -0,0 +1,3 @@ +export * from './article' +export * from './contact' +export * from './money' diff --git a/modules/nuc_entities/atomic/bosons/constants/fields/money.ts b/modules/nuc_entities/atomic/bosons/constants/fields/money.ts new file mode 100644 index 0000000..5e9a0e4 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/constants/fields/money.ts @@ -0,0 +1,39 @@ +import type { EntityFieldInterface, UseFieldsInterface } from 'nucleify' +import { roles } from 'nucleify' + +export function useMoneyFields(): UseFieldsInterface { + const fieldData: readonly [string, string, string][] = [ + ['count', 'field-count', 'input-text'], + ['sender', 'field-sender', 'input-text'], + ['receiver', 'field-receiver', 'input-text'], + ['title', 'field-title', 'input-text'], + ['description', 'field-description', 'textarea'], + ['category', 'field-category', 'input-text'], + ['updated_at', 'field-updated-at', ''], + ['created_at', 'field-created-at', ''], + ] as const + + const createAndEditFields: EntityFieldInterface[] = fieldData + .filter(([name]) => !['created_at', 'updated_at'].includes(name)) + .map(([name, label, type]): EntityFieldInterface => { + const props = + name === 'role' + ? { options: roles, placeholder: 'field-select-role' } + : undefined + + return { name, label, type, props } + }) + + const showFields: readonly { label: string; key: string }[] = fieldData.map( + ([key, label]) => ({ + name: key, + key, + label, + }) + ) + + return { + createAndEditFields, + showFields, + } +} diff --git a/modules/nuc_entities/atomic/bosons/constants/index.ts b/modules/nuc_entities/atomic/bosons/constants/index.ts new file mode 100644 index 0000000..b29bfea --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/constants/index.ts @@ -0,0 +1 @@ +export * from './fields' diff --git a/modules/nuc_entities/atomic/bosons/index.ts b/modules/nuc_entities/atomic/bosons/index.ts new file mode 100644 index 0000000..6eec6bb --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/index.ts @@ -0,0 +1,3 @@ +export * from './constants' +export * from './types' +export * from './utils' diff --git a/modules/nuc_entities/atomic/bosons/types/api/Article/index.ts b/modules/nuc_entities/atomic/bosons/types/api/Article/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/Article/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities/atomic/bosons/types/api/Article/interfaces.ts b/modules/nuc_entities/atomic/bosons/types/api/Article/interfaces.ts new file mode 100644 index 0000000..fcc5a1a --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/Article/interfaces.ts @@ -0,0 +1,22 @@ +import type { + DeleteEntityRequestType, + EditEntityRequestType, + EntityCountResultsType, + EntityResultsType, + GetAllEntitiesRequestType, + GetEntityRequestType, + LoadingRefType, + NucArticleObjectInterface, + StoreEntityRequestType, +} from 'nucleify' + +export interface NucArticleRequestsInterface { + results: EntityResultsType + createdLastWeek: EntityCountResultsType + loading: LoadingRefType + getAllArticles: GetAllEntitiesRequestType + getCountArticlesByCreatedLastWeek: GetEntityRequestType + storeArticle: StoreEntityRequestType + editArticle: EditEntityRequestType + deleteArticle: DeleteEntityRequestType +} diff --git a/modules/nuc_entities/atomic/bosons/types/api/Contact/index.ts b/modules/nuc_entities/atomic/bosons/types/api/Contact/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/Contact/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities/atomic/bosons/types/api/Contact/interfaces.ts b/modules/nuc_entities/atomic/bosons/types/api/Contact/interfaces.ts new file mode 100644 index 0000000..30ecac2 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/Contact/interfaces.ts @@ -0,0 +1,22 @@ +import type { + DeleteEntityRequestType, + EditEntityRequestType, + EntityCountResultsType, + EntityResultsType, + GetAllEntitiesRequestType, + GetEntityRequestType, + LoadingRefType, + NucContactObjectInterface, + StoreEntityRequestType, +} from 'nucleify' + +export interface NucContactRequestsInterface { + results: EntityResultsType + createdLastWeek: EntityCountResultsType + loading: LoadingRefType + getAllContacts: GetAllEntitiesRequestType + getCountContactsByCreatedLastWeek: GetEntityRequestType + storeContact: StoreEntityRequestType + editContact: EditEntityRequestType + deleteContact: DeleteEntityRequestType +} diff --git a/modules/nuc_entities/atomic/bosons/types/api/Money/index.ts b/modules/nuc_entities/atomic/bosons/types/api/Money/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/Money/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities/atomic/bosons/types/api/Money/interfaces.ts b/modules/nuc_entities/atomic/bosons/types/api/Money/interfaces.ts new file mode 100644 index 0000000..fb70724 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/Money/interfaces.ts @@ -0,0 +1,22 @@ +import type { + DeleteEntityRequestType, + EditEntityRequestType, + EntityCountResultsType, + EntityResultsType, + GetAllEntitiesRequestType, + GetEntityRequestType, + LoadingRefType, + NucMoneyObjectInterface, + StoreEntityRequestType, +} from 'nucleify' + +export interface NucMoneyRequestsInterface { + results: EntityResultsType + createdLastWeek: EntityCountResultsType + loading: LoadingRefType + getAllMoney: GetAllEntitiesRequestType + getCountMoneyByCreatedLastWeek: GetEntityRequestType + storeMoney: StoreEntityRequestType + editMoney: EditEntityRequestType + deleteMoney: DeleteEntityRequestType +} diff --git a/modules/nuc_entities/atomic/bosons/types/api/index.ts b/modules/nuc_entities/atomic/bosons/types/api/index.ts new file mode 100644 index 0000000..6ce13db --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/api/index.ts @@ -0,0 +1,3 @@ +export * from './Article' +export * from './Contact' +export * from './Money' diff --git a/modules/nuc_entities/atomic/bosons/types/index.ts b/modules/nuc_entities/atomic/bosons/types/index.ts new file mode 100644 index 0000000..d64bb2b --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/index.ts @@ -0,0 +1,2 @@ +export * from './api' +export * from './object' diff --git a/modules/nuc_entities/atomic/bosons/types/object/Article/index.ts b/modules/nuc_entities/atomic/bosons/types/object/Article/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/Article/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities/atomic/bosons/types/object/Article/interfaces.ts b/modules/nuc_entities/atomic/bosons/types/object/Article/interfaces.ts new file mode 100644 index 0000000..37e8fa3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/Article/interfaces.ts @@ -0,0 +1,9 @@ +export interface NucArticleObjectInterface { + id?: number + user_id?: number + title: string + description: string + created_at?: string + updated_at?: string + category: string +} diff --git a/modules/nuc_entities/atomic/bosons/types/object/Contact/index.ts b/modules/nuc_entities/atomic/bosons/types/object/Contact/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/Contact/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities/atomic/bosons/types/object/Contact/interfaces.ts b/modules/nuc_entities/atomic/bosons/types/object/Contact/interfaces.ts new file mode 100644 index 0000000..a9cc53f --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/Contact/interfaces.ts @@ -0,0 +1,16 @@ +export interface NucContactObjectInterface { + id?: number + user_id?: number + first_name: string + last_name?: string + full_name?: string + email: string + personal_phone?: string + work_phone?: string + address?: string + birthday?: string + contact_groups?: string + role?: string + created_at?: string + updated_at?: string +} diff --git a/modules/nuc_entities/atomic/bosons/types/object/Money/index.ts b/modules/nuc_entities/atomic/bosons/types/object/Money/index.ts new file mode 100644 index 0000000..f1444d3 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/Money/index.ts @@ -0,0 +1 @@ +export * from './interfaces' diff --git a/modules/nuc_entities/atomic/bosons/types/object/Money/interfaces.ts b/modules/nuc_entities/atomic/bosons/types/object/Money/interfaces.ts new file mode 100644 index 0000000..c01e016 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/Money/interfaces.ts @@ -0,0 +1,12 @@ +export interface NucMoneyObjectInterface { + id?: number + user_id?: number + sender: string + receiver: string + count: number + title: string + description: string + category: string + created_at: string + updated_at?: string +} diff --git a/modules/nuc_entities/atomic/bosons/types/object/index.ts b/modules/nuc_entities/atomic/bosons/types/object/index.ts new file mode 100644 index 0000000..6ce13db --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/types/object/index.ts @@ -0,0 +1,3 @@ +export * from './Article' +export * from './Contact' +export * from './Money' diff --git a/modules/nuc_entities/atomic/bosons/utils/api/article_requests.ts b/modules/nuc_entities/atomic/bosons/utils/api/article_requests.ts new file mode 100644 index 0000000..ecd655d --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/utils/api/article_requests.ts @@ -0,0 +1,106 @@ +'use client' + +import { useState } from 'react' + +import type { + CloseDialogType, + NucArticleObjectInterface, + UseLoadingInterface, +} from 'nucleify' +import { + apiHandle, + sessionStorageGetItem, + useApiSuccess, + useLoading, +} from 'nucleify' + +import type { NucArticleRequestsInterface } from '../../types/api' + +export function articleRequests( + close?: CloseDialogType +): NucArticleRequestsInterface { + const [results, setResults] = useState([]) + const [createdLastWeek, setCreatedLastWeek] = useState(0) + + const { loading, setLoading }: UseLoadingInterface = useLoading() + const { apiSuccess } = useApiSuccess() + + async function getAllArticles(loading?: boolean): Promise { + await apiHandle({ + url: '/api/articles', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucArticleObjectInterface[]) => { + setResults(response) + }, + }) + } + + async function getCountArticlesByCreatedLastWeek( + loading?: boolean + ): Promise { + await apiHandle({ + url: '/api/articles/count-by-created-last-week', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: number) => { + setCreatedLastWeek(response) + }, + }) + } + + async function storeArticle( + data: NucArticleObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/articles', + method: 'POST', + data: { + user_id: sessionStorageGetItem('user_id'), + ...data, + }, + onSuccess: (response: NucArticleObjectInterface) => { + apiSuccess(response, getData, close, 'create') + }, + }) + } + + async function editArticle( + data: NucArticleObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/articles', + method: 'PUT', + data: data, + id: data.id, + onSuccess: (response: NucArticleObjectInterface) => { + apiSuccess(response, getData, close, 'edit') + }, + }) + } + + async function deleteArticle( + id: number, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/articles', + method: 'DELETE', + id, + onSuccess: (response: NucArticleObjectInterface) => { + apiSuccess(response, getData, close, 'delete') + }, + }) + } + + return { + results, + createdLastWeek, + loading, + getAllArticles, + getCountArticlesByCreatedLastWeek, + storeArticle, + editArticle, + deleteArticle, + } +} diff --git a/modules/nuc_entities/atomic/bosons/utils/api/contact_requests.ts b/modules/nuc_entities/atomic/bosons/utils/api/contact_requests.ts new file mode 100644 index 0000000..eab63fb --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/utils/api/contact_requests.ts @@ -0,0 +1,106 @@ +'use client' + +import { useState } from 'react' + +import type { + CloseDialogType, + NucContactObjectInterface, + UseLoadingInterface, +} from 'nucleify' +import { + apiHandle, + sessionStorageGetItem, + useApiSuccess, + useLoading, +} from 'nucleify' + +import type { NucContactRequestsInterface } from '../../types/api' + +export function contactRequests( + close?: CloseDialogType +): NucContactRequestsInterface { + const [results, setResults] = useState([]) + const [createdLastWeek, setCreatedLastWeek] = useState(0) + + const { loading, setLoading }: UseLoadingInterface = useLoading() + const { apiSuccess } = useApiSuccess() + + async function getAllContacts(loading?: boolean): Promise { + await apiHandle({ + url: '/api/contacts', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucContactObjectInterface[]) => { + setResults(response) + }, + }) + } + + async function getCountContactsByCreatedLastWeek( + loading?: boolean + ): Promise { + await apiHandle({ + url: '/api/contacts/count-by-created-last-week', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: number) => { + setCreatedLastWeek(response) + }, + }) + } + + async function storeContact( + data: NucContactObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/contacts', + method: 'POST', + data: { + user_id: sessionStorageGetItem('user_id'), + ...data, + }, + onSuccess: (response: NucContactObjectInterface) => { + apiSuccess(response, getData, close, 'create') + }, + }) + } + + async function editContact( + data: NucContactObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/contacts', + method: 'PUT', + data, + id: data.id, + onSuccess: (response: NucContactObjectInterface) => { + apiSuccess(response, getData, close, 'edit') + }, + }) + } + + async function deleteContact( + id: number, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/contacts', + method: 'DELETE', + id, + onSuccess: (response: NucContactObjectInterface) => { + apiSuccess(response, getData, close, 'delete') + }, + }) + } + + return { + results, + createdLastWeek, + loading, + getAllContacts, + getCountContactsByCreatedLastWeek, + storeContact, + editContact, + deleteContact, + } +} diff --git a/modules/nuc_entities/atomic/bosons/utils/api/index.ts b/modules/nuc_entities/atomic/bosons/utils/api/index.ts new file mode 100644 index 0000000..b840ed5 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/utils/api/index.ts @@ -0,0 +1,3 @@ +export * from './article_requests' +export * from './contact_requests' +export * from './money_requests' diff --git a/modules/nuc_entities/atomic/bosons/utils/api/money_requests.ts b/modules/nuc_entities/atomic/bosons/utils/api/money_requests.ts new file mode 100644 index 0000000..16c6216 --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/utils/api/money_requests.ts @@ -0,0 +1,106 @@ +'use client' + +import { useState } from 'react' + +import type { + CloseDialogType, + NucMoneyObjectInterface, + UseLoadingInterface, +} from 'nucleify' +import { + apiHandle, + sessionStorageGetItem, + useApiSuccess, + useLoading, +} from 'nucleify' + +import type { NucMoneyRequestsInterface } from '../../types/api' + +export function moneyRequests( + close?: CloseDialogType +): NucMoneyRequestsInterface { + const [results, setResults] = useState([]) + const [createdLastWeek, setCreatedLastWeek] = useState(0) + + const { loading, setLoading }: UseLoadingInterface = useLoading() + const { apiSuccess } = useApiSuccess() + + async function getAllMoney(loading?: boolean): Promise { + await apiHandle({ + url: '/api/money', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: NucMoneyObjectInterface[]) => { + setResults(response) + }, + }) + } + + async function getCountMoneyByCreatedLastWeek( + loading?: boolean + ): Promise { + await apiHandle({ + url: '/api/money/count-by-created-last-week', + setLoading: loading ? setLoading : undefined, + onSuccess: (response: number) => { + setCreatedLastWeek(response) + }, + }) + } + + async function storeMoney( + data: NucMoneyObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/money', + method: 'POST', + data: { + user_id: sessionStorageGetItem('user_id'), + ...data, + }, + onSuccess: (response: NucMoneyObjectInterface) => { + apiSuccess(response, getData, close, 'create') + }, + }) + } + + async function editMoney( + data: NucMoneyObjectInterface, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/money', + method: 'PUT', + data, + id: data.id, + onSuccess: (response: NucMoneyObjectInterface) => { + apiSuccess(response, getData, close, 'edit') + }, + }) + } + + async function deleteMoney( + id: number, + getData: () => Promise + ): Promise { + await apiHandle({ + url: '/api/money', + method: 'DELETE', + id, + onSuccess: (response: NucMoneyObjectInterface) => { + apiSuccess(response, getData, close, 'delete') + }, + }) + } + + return { + results, + createdLastWeek, + loading, + getAllMoney, + getCountMoneyByCreatedLastWeek, + storeMoney, + editMoney, + deleteMoney, + } +} diff --git a/modules/nuc_entities/atomic/bosons/utils/index.ts b/modules/nuc_entities/atomic/bosons/utils/index.ts new file mode 100644 index 0000000..3318fdb --- /dev/null +++ b/modules/nuc_entities/atomic/bosons/utils/index.ts @@ -0,0 +1 @@ +export * from './api' diff --git a/modules/nuc_entities/atomic/index.ts b/modules/nuc_entities/atomic/index.ts new file mode 100644 index 0000000..677b9c2 --- /dev/null +++ b/modules/nuc_entities/atomic/index.ts @@ -0,0 +1,3 @@ +export * from './bosons' +export * from './pages' +export * from './templates' diff --git a/modules/nuc_entities/atomic/pages/Article/index.ts b/modules/nuc_entities/atomic/pages/Article/index.ts new file mode 100644 index 0000000..7b71f81 --- /dev/null +++ b/modules/nuc_entities/atomic/pages/Article/index.ts @@ -0,0 +1 @@ +export { NucArticlePage } from './index.tsx' diff --git a/modules/nuc_entities/atomic/pages/Article/index.tsx b/modules/nuc_entities/atomic/pages/Article/index.tsx new file mode 100644 index 0000000..55cce24 --- /dev/null +++ b/modules/nuc_entities/atomic/pages/Article/index.tsx @@ -0,0 +1,25 @@ +'use client' + +import type { JSX } from 'react' +import { useEffect } from 'react' + +import { articleRequests, NucArticleDashboard, useNucDialog } from 'nucleify' + +export function NucArticlePage(): JSX.Element { + const { closeDialog } = useNucDialog() + const { loading, results, getAllArticles } = articleRequests(closeDialog) + + useEffect(() => { + void getAllArticles(true).catch(() => undefined) + }, []) + + return ( +
+ +
+ ) +} diff --git a/modules/nuc_entities/atomic/pages/Contact/index.ts b/modules/nuc_entities/atomic/pages/Contact/index.ts new file mode 100644 index 0000000..ee8012a --- /dev/null +++ b/modules/nuc_entities/atomic/pages/Contact/index.ts @@ -0,0 +1 @@ +export { NucContactPage } from './index.tsx' diff --git a/modules/nuc_entities/atomic/pages/Contact/index.tsx b/modules/nuc_entities/atomic/pages/Contact/index.tsx new file mode 100644 index 0000000..bfbbaab --- /dev/null +++ b/modules/nuc_entities/atomic/pages/Contact/index.tsx @@ -0,0 +1,25 @@ +'use client' + +import type { JSX } from 'react' +import { useEffect } from 'react' + +import { contactRequests, NucContactDashboard, useNucDialog } from 'nucleify' + +export function NucContactPage(): JSX.Element { + const { closeDialog } = useNucDialog() + const { loading, results, getAllContacts } = contactRequests(closeDialog) + + useEffect(() => { + void getAllContacts(true).catch(() => undefined) + }, []) + + return ( +
+ +
+ ) +} diff --git a/modules/nuc_entities/atomic/pages/General/index.ts b/modules/nuc_entities/atomic/pages/General/index.ts new file mode 100644 index 0000000..5625e03 --- /dev/null +++ b/modules/nuc_entities/atomic/pages/General/index.ts @@ -0,0 +1 @@ +export { NucEntitiesPage } from './index.tsx' diff --git a/modules/nuc_entities/atomic/pages/General/index.tsx b/modules/nuc_entities/atomic/pages/General/index.tsx new file mode 100644 index 0000000..6c06045 --- /dev/null +++ b/modules/nuc_entities/atomic/pages/General/index.tsx @@ -0,0 +1,134 @@ +'use client' + +import { usePathname } from 'next/navigation' +import type { JSX } from 'react' +import { useEffect, useMemo, useState } from 'react' + +import type { TileInterface } from 'nucleify' +import { + AdTile, + articleRequests, + contactRequests, + moneyRequests, + NucArticleDashboard, + NucContactDashboard, + NucMoneyDashboard, + t, +} from 'nucleify' + +export function NucEntitiesPage(): JSX.Element { + const pathname = usePathname() + const lang = pathname.split('/').filter(Boolean).at(0) || 'en' + const { + results: articles, + createdLastWeek: articlesCreatedLastWeek, + loading: articlesLoading, + getAllArticles, + getCountArticlesByCreatedLastWeek, + } = articleRequests() + const { + results: contacts, + createdLastWeek: contactsCreatedLastWeek, + loading: contactsLoading, + getAllContacts, + getCountContactsByCreatedLastWeek, + } = contactRequests() + const { + results: money, + createdLastWeek: moneyCreatedLastWeek, + loading: moneyLoading, + getAllMoney, + getCountMoneyByCreatedLastWeek, + } = moneyRequests() + const [allLoaded, setAllLoaded] = useState(false) + + useEffect(() => { + void getAllArticles(true).catch(() => undefined) + void getAllContacts(true).catch(() => undefined) + void getAllMoney(true).catch(() => undefined) + void getCountArticlesByCreatedLastWeek().catch(() => undefined) + void getCountContactsByCreatedLastWeek().catch(() => undefined) + void getCountMoneyByCreatedLastWeek().catch(() => undefined) + }, []) + + useEffect(() => { + if (!articlesLoading && !contactsLoading && !moneyLoading) { + const timeout = setTimeout(() => { + setAllLoaded(true) + }, 200) + return () => clearTimeout(timeout) + } + setAllLoaded(false) + return + }, [articlesLoading, contactsLoading, moneyLoading]) + + const entities = useMemo( + () => [ + { + href: `/${lang}/entities/articles`, + header: t('admin-tile-articles'), + count: articles?.length || 0, + icon: 'prime:comment', + countSecondary: articlesCreatedLastWeek || 0, + textSecondary: t('admin-tile-this-week'), + }, + { + href: `/${lang}/entities/contacts`, + header: t('admin-tile-contacts'), + count: contacts?.length || 0, + icon: 'prime:user', + countSecondary: contactsCreatedLastWeek || 0, + textSecondary: t('admin-tile-this-week'), + }, + { + href: `/${lang}/entities/money`, + header: t('admin-tile-money'), + count: money?.length || 0, + icon: 'prime:dollar', + countSecondary: moneyCreatedLastWeek || 0, + textSecondary: t('admin-tile-this-week'), + }, + ], + [ + articles, + contacts, + money, + articlesCreatedLastWeek, + contactsCreatedLastWeek, + moneyCreatedLastWeek, + lang, + ] + ) + + return ( +
+
+ {entities.map((entity) => ( + + ))} +
+ + + +
+ ) +} diff --git a/modules/nuc_entities/atomic/pages/Money/index.ts b/modules/nuc_entities/atomic/pages/Money/index.ts new file mode 100644 index 0000000..0ad8ebf --- /dev/null +++ b/modules/nuc_entities/atomic/pages/Money/index.ts @@ -0,0 +1 @@ +export { NucMoneyPage } from './index.tsx' diff --git a/modules/nuc_entities/atomic/pages/Money/index.tsx b/modules/nuc_entities/atomic/pages/Money/index.tsx new file mode 100644 index 0000000..75bc121 --- /dev/null +++ b/modules/nuc_entities/atomic/pages/Money/index.tsx @@ -0,0 +1,25 @@ +'use client' + +import type { JSX } from 'react' +import { useEffect } from 'react' + +import { moneyRequests, NucMoneyDashboard, useNucDialog } from 'nucleify' + +export function NucMoneyPage(): JSX.Element { + const { closeDialog } = useNucDialog() + const { loading, results, getAllMoney } = moneyRequests(closeDialog) + + useEffect(() => { + void getAllMoney(true).catch(() => undefined) + }, []) + + return ( +
+ +
+ ) +} diff --git a/modules/nuc_entities/atomic/pages/index.ts b/modules/nuc_entities/atomic/pages/index.ts new file mode 100644 index 0000000..ef5a5d6 --- /dev/null +++ b/modules/nuc_entities/atomic/pages/index.ts @@ -0,0 +1,4 @@ +export * from './Article' +export * from './Contact' +export * from './General' +export * from './Money' diff --git a/modules/nuc_entities/atomic/templates/Dashboard/Article.tsx b/modules/nuc_entities/atomic/templates/Dashboard/Article.tsx new file mode 100644 index 0000000..5f73d7f --- /dev/null +++ b/modules/nuc_entities/atomic/templates/Dashboard/Article.tsx @@ -0,0 +1,125 @@ +'use client' + +import type { JSX } from 'react' +import { useMemo } from 'react' + +import type { NucArticleObjectInterface, NucDashboardInterface } from 'nucleify' +import { + articleRequests, + NucDialog, + t, + useArticleFields, + useNucDialog, +} from 'nucleify' + +import { NucEntityDataTable } from '../../../../nuc_datatable/atomic/templates/entity-datatable' + +type ArticleDashboardProps = Omit & { + data: NucArticleObjectInterface[] +} + +export function NucArticleDashboard({ + data, + getData, + loading, +}: ArticleDashboardProps): JSX.Element { + const safeData = data ?? [] + const { + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + selectedObject, + openDialog, + closeDialog, + } = useNucDialog() + const { createAndEditFields, showFields } = useArticleFields() + const { deleteArticle, storeArticle, editArticle } = + articleRequests(closeDialog) + + const dialogs = useMemo( + () => [ + { + entity: 'article', + action: 'show', + visible: visibleShow, + cancelButtonLabel: t('common-close'), + fields: showFields, + }, + { + entity: 'article', + action: 'delete', + visible: visibleDelete, + title: t('entity-article-delete'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: deleteArticle, + getData, + }, + { + entity: 'article', + action: 'create', + visible: visibleCreate, + title: t('entity-article-create'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: storeArticle, + getData, + fields: createAndEditFields, + }, + { + entity: 'article', + action: 'edit', + visible: visibleEdit, + title: t('entity-article-edit'), + confirmButtonLabel: t('common-update'), + cancelButtonLabel: t('common-cancel'), + confirm: editArticle, + getData, + fields: createAndEditFields, + }, + ], + [ + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + showFields, + createAndEditFields, + deleteArticle, + storeArticle, + editArticle, + getData, + ] + ) + + return ( +
+ + + {dialogs.map((dialog) => ( + + ))} +
+ ) +} diff --git a/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx b/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx new file mode 100644 index 0000000..c1e8d12 --- /dev/null +++ b/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx @@ -0,0 +1,125 @@ +'use client' + +import type { JSX } from 'react' +import { useMemo } from 'react' + +import type { NucContactObjectInterface, NucDashboardInterface } from 'nucleify' +import { + contactRequests, + NucDialog, + t, + useContactFields, + useNucDialog, +} from 'nucleify' + +import { NucEntityDataTable } from '../../../../nuc_datatable/atomic/templates/entity-datatable' + +type ContactDashboardProps = Omit & { + data: NucContactObjectInterface[] +} + +export function NucContactDashboard({ + data, + getData, + loading, +}: ContactDashboardProps): JSX.Element { + const safeData = data ?? [] + const { + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + selectedObject, + openDialog, + closeDialog, + } = useNucDialog() + const { createAndEditFields, showFields } = useContactFields() + const { deleteContact, storeContact, editContact } = + contactRequests(closeDialog) + + const dialogs = useMemo( + () => [ + { + entity: 'contact', + action: 'show', + visible: visibleShow, + cancelButtonLabel: t('common-close'), + fields: showFields, + }, + { + entity: 'contact', + action: 'delete', + visible: visibleDelete, + title: t('entity-contact-delete'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: deleteContact, + getData, + }, + { + entity: 'contact', + action: 'create', + visible: visibleCreate, + title: t('entity-contact-create'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: storeContact, + getData, + fields: createAndEditFields, + }, + { + entity: 'contact', + action: 'edit', + visible: visibleEdit, + title: t('entity-contact-edit'), + confirmButtonLabel: t('common-update'), + cancelButtonLabel: t('common-cancel'), + confirm: editContact, + getData, + fields: createAndEditFields, + }, + ], + [ + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + showFields, + createAndEditFields, + deleteContact, + storeContact, + editContact, + getData, + ] + ) + + return ( +
+ + + {dialogs.map((dialog) => ( + + ))} +
+ ) +} diff --git a/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx b/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx new file mode 100644 index 0000000..496d9df --- /dev/null +++ b/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx @@ -0,0 +1,124 @@ +'use client' + +import type { JSX } from 'react' +import { useMemo } from 'react' + +import type { NucDashboardInterface, NucMoneyObjectInterface } from 'nucleify' +import { + moneyRequests, + NucDialog, + t, + useMoneyFields, + useNucDialog, +} from 'nucleify' + +import { NucEntityDataTable } from '../../../../nuc_datatable/atomic/templates/entity-datatable' + +type MoneyDashboardProps = Omit & { + data: NucMoneyObjectInterface[] +} + +export function NucMoneyDashboard({ + data, + getData, + loading, +}: MoneyDashboardProps): JSX.Element { + const safeData = data ?? [] + const { + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + selectedObject, + openDialog, + closeDialog, + } = useNucDialog() + const { createAndEditFields, showFields } = useMoneyFields() + const { deleteMoney, storeMoney, editMoney } = moneyRequests(closeDialog) + + const dialogs = useMemo( + () => [ + { + entity: 'money', + action: 'show', + visible: visibleShow, + cancelButtonLabel: t('common-close'), + fields: showFields, + }, + { + entity: 'money', + action: 'delete', + visible: visibleDelete, + title: t('entity-money-delete'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: deleteMoney, + getData, + }, + { + entity: 'money', + action: 'create', + visible: visibleCreate, + title: t('entity-money-create'), + confirmButtonLabel: t('common-confirm'), + cancelButtonLabel: t('common-cancel'), + confirm: storeMoney, + getData, + fields: createAndEditFields, + }, + { + entity: 'money', + action: 'edit', + visible: visibleEdit, + title: t('entity-money-edit'), + confirmButtonLabel: t('common-update'), + cancelButtonLabel: t('common-cancel'), + confirm: editMoney, + getData, + fields: createAndEditFields, + }, + ], + [ + visibleShow, + visibleCreate, + visibleEdit, + visibleDelete, + showFields, + createAndEditFields, + deleteMoney, + storeMoney, + editMoney, + getData, + ] + ) + + return ( +
+ + + {dialogs.map((dialog) => ( + + ))} +
+ ) +} diff --git a/modules/nuc_entities/atomic/templates/Dashboard/index.ts b/modules/nuc_entities/atomic/templates/Dashboard/index.ts new file mode 100644 index 0000000..9931207 --- /dev/null +++ b/modules/nuc_entities/atomic/templates/Dashboard/index.ts @@ -0,0 +1,3 @@ +export { NucArticleDashboard } from './Article.tsx' +export { NucContactDashboard } from './Contact.tsx' +export { NucMoneyDashboard } from './Money.tsx' diff --git a/modules/nuc_entities/atomic/templates/index.ts b/modules/nuc_entities/atomic/templates/index.ts new file mode 100644 index 0000000..f359847 --- /dev/null +++ b/modules/nuc_entities/atomic/templates/index.ts @@ -0,0 +1 @@ +export * from './Dashboard' diff --git a/modules/nuc_entities/config.json b/modules/nuc_entities/config.json new file mode 100644 index 0000000..e084612 --- /dev/null +++ b/modules/nuc_entities/config.json @@ -0,0 +1,8 @@ +{ + "name": "nuc_entities", + "description": "Module that contains entity functions.", + "version": "0.6.2", + "category": "core", + "installed": true, + "enabled": true +} diff --git a/modules/nuc_entities/index.ts b/modules/nuc_entities/index.ts new file mode 100644 index 0000000..6d00745 --- /dev/null +++ b/modules/nuc_entities/index.ts @@ -0,0 +1,13 @@ +/** + * Module's main file export + */ +export * from './nuc_entities' + +/** + * Folders exports + */ +export * from './atomic' + +/** + * File exports + */ diff --git a/modules/nuc_entities/nuc_entities.ts b/modules/nuc_entities/nuc_entities.ts new file mode 100644 index 0000000..7674dc9 --- /dev/null +++ b/modules/nuc_entities/nuc_entities.ts @@ -0,0 +1,19 @@ +import { + NucArticleDashboard, + NucArticlePage, + NucContactDashboard, + NucContactPage, + NucEntitiesPage, + NucMoneyDashboard, + NucMoneyPage, +} from './atomic' + +export function registerNucEntities(): void { + void NucArticlePage + void NucContactPage + void NucEntitiesPage + void NucMoneyPage + void NucArticleDashboard + void NucContactDashboard + void NucMoneyDashboard +} From 82bad5cd6f1676d9388fa549b7ccc262b2a8265a Mon Sep 17 00:00:00 2001 From: Mateusz Szymanski Date: Sun, 26 Apr 2026 04:27:55 +0200 Subject: [PATCH 2/2] feat(nuc_entities): enhance entity dashboards with chart components and improve error handling --- modules/nuc_api/utils/api_request.ts | 46 ++++++++++++++-- modules/nuc_api/utils/use_api_errors.ts | 7 +-- .../atomic/template/entity-chart/index.tsx | 9 ++- .../templates/entity-datatable-card/index.tsx | 15 ++++- modules/nuc_dialog/index.tsx | 55 +++++++++---------- .../atomic/pages/Article/index.tsx | 19 ++++++- .../atomic/pages/Contact/index.tsx | 19 ++++++- .../atomic/pages/General/index.tsx | 27 +++++++-- .../nuc_entities/atomic/pages/Money/index.tsx | 19 ++++++- .../atomic/templates/Dashboard/Article.tsx | 10 ++-- .../atomic/templates/Dashboard/Contact.tsx | 10 ++-- .../atomic/templates/Dashboard/Money.tsx | 10 ++-- 12 files changed, 178 insertions(+), 68 deletions(-) diff --git a/modules/nuc_api/utils/api_request.ts b/modules/nuc_api/utils/api_request.ts index c55b750..584cac8 100644 --- a/modules/nuc_api/utils/api_request.ts +++ b/modules/nuc_api/utils/api_request.ts @@ -16,6 +16,10 @@ function resolveApiUrl(url: string): string { return `${baseUrl}${url}` } +function stripApiPrefix(url: string): string { + return url.startsWith('/api/') ? url.slice(4) : url +} + export async function apiRequest( url: string, method: HttpMethodType = 'GET', @@ -54,13 +58,35 @@ export async function apiRequest( const queryString = searchParams.toString() const requestUrl = queryString ? `${finalApiUrl}?${queryString}` : finalApiUrl - const response = await fetch(requestUrl, { + let response = await fetch(requestUrl, { method, body: data ? JSON.stringify(data) : undefined, headers, credentials: 'include', }) + // Fallback for environments where backend routes are exposed without `/api`. + if ( + !response.ok && + response.status === 404 && + !/^https?:\/\//.test(finalUrl) + ) { + const fallbackUrl = stripApiPrefix(finalUrl) + if (fallbackUrl !== finalUrl) { + const resolvedFallbackUrl = resolveApiUrl(fallbackUrl) + const fallbackRequestUrl = queryString + ? `${resolvedFallbackUrl}?${queryString}` + : resolvedFallbackUrl + + response = await fetch(fallbackRequestUrl, { + method, + body: data ? JSON.stringify(data) : undefined, + headers, + credentials: 'include', + }) + } + } + if (!response.ok) { let errorData: unknown = null try { @@ -68,14 +94,26 @@ export async function apiRequest( } catch { errorData = { error: response.statusText } } - - throw { + const responseData = + errorData && typeof errorData === 'object' + ? (errorData as { error?: string; errors?: string }) + : null + const message = + responseData?.error || + responseData?.errors || + response.statusText || + `Request failed with status ${response.status}` + const requestError = new Error(message) + + Object.assign(requestError, { response: { status: response.status, data: errorData, }, data: errorData, - } + }) + + throw requestError } return (await response.json()) as ApiResponseType diff --git a/modules/nuc_api/utils/use_api_errors.ts b/modules/nuc_api/utils/use_api_errors.ts index 2d17bb6..ec7f6eb 100644 --- a/modules/nuc_api/utils/use_api_errors.ts +++ b/modules/nuc_api/utils/use_api_errors.ts @@ -33,21 +33,20 @@ export function useApiErrors(): UseApiErrorsInterface { flashToast('An unknown error occurred', 'error') } - throw error + return } if (error instanceof Error) { flashToast(error.message, 'error') - throw error + return } if (typeof error === 'string') { flashToast(error, 'error') - throw new Error(error) + return } flashToast('An unknown error occurred', 'error') - throw new Error('An unknown error occurred') } return { diff --git a/modules/nuc_charts/atomic/template/entity-chart/index.tsx b/modules/nuc_charts/atomic/template/entity-chart/index.tsx index caf2e83..82316a6 100644 --- a/modules/nuc_charts/atomic/template/entity-chart/index.tsx +++ b/modules/nuc_charts/atomic/template/entity-chart/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { ChartData } from 'chart.js' -import React, { useEffect, useMemo, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { AdChart, type ChartType } from 'nucleify' @@ -11,6 +11,7 @@ export const NucEntityChart: React.FC = (props) => { const { setChartData, setChartOptions } = useChart() const [chartData, setChartDataState] = useState(null) + const lastDataKeyRef = useRef('') const chartOptions = useMemo(() => { if (!props.type) return {} @@ -34,7 +35,11 @@ export const NucEntityChart: React.FC = (props) => { ) if (dataToSet) { - setChartDataState(dataToSet) + const dataKey = JSON.stringify(dataToSet) + if (dataKey !== lastDataKeyRef.current) { + lastDataKeyRef.current = dataKey + setChartDataState(dataToSet) + } } } diff --git a/modules/nuc_datatable/atomic/templates/entity-datatable-card/index.tsx b/modules/nuc_datatable/atomic/templates/entity-datatable-card/index.tsx index 132234d..2de542e 100644 --- a/modules/nuc_datatable/atomic/templates/entity-datatable-card/index.tsx +++ b/modules/nuc_datatable/atomic/templates/entity-datatable-card/index.tsx @@ -2,7 +2,7 @@ import { Button } from 'primereact/button' import { Card } from 'primereact/card' import type { DataTableFilterMeta } from 'primereact/datatable' import { Skeleton } from 'primereact/skeleton' -import React, { useMemo, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import { NucEntityDataTable } from '../entity-datatable' @@ -19,6 +19,17 @@ export const NucEntityDataTableCard: React.FC< const [shareDialogVisible, setShareDialogVisible] = useState(false) const [selectedItems, setSelectedItems] = useState([]) + const handleSelectedUpdate = useCallback((selected: unknown[]) => { + setSelectedItems((prev) => { + if ( + prev.length === selected.length && + prev.every((item, index) => item === selected[index]) + ) { + return prev + } + return selected + }) + }, []) type ColumnWithField = { field?: string } const specificColumns = useMemo(() => { @@ -124,7 +135,7 @@ export const NucEntityDataTableCard: React.FC< totalRecords: '{totalRecords}', })} selection={selectedItems} - onSelectedUpdate={(selected) => setSelectedItems(selected)} + onSelectedUpdate={handleSelectedUpdate} /> )} diff --git a/modules/nuc_dialog/index.tsx b/modules/nuc_dialog/index.tsx index a8ec329..3b268d4 100644 --- a/modules/nuc_dialog/index.tsx +++ b/modules/nuc_dialog/index.tsx @@ -72,6 +72,9 @@ export function NucDialog(props: NucDialogInterface) { {...rest} modal={props.modal ?? true} showHeader={props.showHeader ?? true} + onHide={() => { + if (action) close?.(action) + }} className={`nuc-dialog ${action || ''}`} header={ action === 'show' && selectedObject ? ( @@ -117,13 +120,16 @@ export function NucDialog(props: NucDialogInterface) { const FieldComponent = getComponent( field.type as ComponentType ) as React.ElementType + const isSelectLike = isSelectOrDatePicker(field.type) + const isPasswordConfirmation = + field.name === 'password_confirmation' return (
{ - void getAllArticles(true).catch(() => undefined) + void getAllArticles(true) }, []) return (
+ { - void getAllContacts(true).catch(() => undefined) + void getAllContacts(true) }, []) return (
+ (false) useEffect(() => { - void getAllArticles(true).catch(() => undefined) - void getAllContacts(true).catch(() => undefined) - void getAllMoney(true).catch(() => undefined) - void getCountArticlesByCreatedLastWeek().catch(() => undefined) - void getCountContactsByCreatedLastWeek().catch(() => undefined) - void getCountMoneyByCreatedLastWeek().catch(() => undefined) + void getAllArticles(true) + void getAllContacts(true) + void getAllMoney(true) + void getCountArticlesByCreatedLastWeek() + void getCountContactsByCreatedLastWeek() + void getCountMoneyByCreatedLastWeek() }, []) useEffect(() => { @@ -114,6 +116,19 @@ export function NucEntitiesPage(): JSX.Element { ))}
+ { - void getAllMoney(true).catch(() => undefined) + void getAllMoney(true) }, []) return (
+ & { data: NucArticleObjectInterface[] } @@ -95,13 +94,14 @@ export function NucArticleDashboard({ return (
- {dialogs.map((dialog) => ( diff --git a/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx b/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx index c1e8d12..d84de4f 100644 --- a/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx +++ b/modules/nuc_entities/atomic/templates/Dashboard/Contact.tsx @@ -7,13 +7,12 @@ import type { NucContactObjectInterface, NucDashboardInterface } from 'nucleify' import { contactRequests, NucDialog, + NucEntityDataTableCard, t, useContactFields, useNucDialog, } from 'nucleify' -import { NucEntityDataTable } from '../../../../nuc_datatable/atomic/templates/entity-datatable' - type ContactDashboardProps = Omit & { data: NucContactObjectInterface[] } @@ -95,13 +94,14 @@ export function NucContactDashboard({ return (
- {dialogs.map((dialog) => ( diff --git a/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx b/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx index 496d9df..59f381e 100644 --- a/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx +++ b/modules/nuc_entities/atomic/templates/Dashboard/Money.tsx @@ -7,13 +7,12 @@ import type { NucDashboardInterface, NucMoneyObjectInterface } from 'nucleify' import { moneyRequests, NucDialog, + NucEntityDataTableCard, t, useMoneyFields, useNucDialog, } from 'nucleify' -import { NucEntityDataTable } from '../../../../nuc_datatable/atomic/templates/entity-datatable' - type MoneyDashboardProps = Omit & { data: NucMoneyObjectInterface[] } @@ -94,13 +93,14 @@ export function NucMoneyDashboard({ return (
- {dialogs.map((dialog) => (