diff --git a/README.md b/README.md index b3f5e9696..361b9afd8 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ emaui/ ``` ### libs/api -- `base-api/` — RTK Query `createApi` instance with `prepareHeaders` that injects the Bearer token from Redux state or localStorage. -- `session/` — `resolveTokenFromStorage()` reads from `localStorage` keys or `auth-token` cookie. `resolveSessionContext()` merges Redux state token with storage fallback. +- `base-api/` — RTK Query `createApi` instance with `prepareHeaders` that injects the Bearer token from Redux state or storage. +- `session/` — `resolveTokenFromStorage()` reads the `auth-token` cookie first, falling back to `localStorage` for legacy pre-migration sessions. `resolveSessionContext()` merges Redux state token with storage fallback. - `query-and-mutation/` — Generic `useApiQuery` / `useApiMutation` wrappers for one-off API calls without defining a dedicated endpoint file. ### libs/ui @@ -55,10 +55,10 @@ emaui/ 1. User submits the login form (LoginForm / LoginPage). 2. The form calls the `login` RTK Query mutation (backoffice) or a plain `fetch` (portal). -3. On success, `loginSuccess` action is dispatched → Redux `auth` slice stores `token` and `user`; `authStorage.setToken()` persists the token to `localStorage`. +3. On success, `loginSuccess` action is dispatched → Redux `auth` slice stores `token` and `user`; `authStorage.setToken()` persists the token to a cookie (both apps call `configureAuthStorage(prefix, true)`). 4. `baseApi`'s `prepareHeaders` reads the token via `resolveSessionContext(getState())` and attaches `Authorization: Bearer ` to every RTK Query request. -5. `ProtectedRoute` checks `localStorage` for the token key on every navigation — if absent, redirects to `/login`. -6. `logout` action clears Redux state and calls `authStorage.clear()` to remove all localStorage keys. +5. `ProtectedRoute` checks `authStorage`/the token cookie on every navigation — if absent, redirects to `/login`. +6. `logout` action clears Redux state and calls `authStorage.clear()` to remove all auth cookies. --- @@ -90,6 +90,8 @@ npm run dev:all |---|---|---|---| | `VITE_BASE_API_URL` | Yes | `http://localhost:3000` | Base URL for all API requests | | `VITE_ENABLE_DEVELOPER_TOOLS` | No | `true` | Toggle Redux DevTools | +| `VITE_PAYMENT_API_URL` | Yes (portal) | — | Base URL for the payment service (portal `payment` feature). Can be a same-origin path if a backend proxy is later placed in front of it | +| `VITE_PAYMENT_SERVICE_TOKEN` | Yes (portal) | — | Sent as `x-service-token` on every payment request. Note: bundled `VITE_*` values are public in the built app, not secret | | `PORTAL_PORT` | No | `4200` | Docker host port for portal | | `BACKOFFICE_PORT` | No | `4201` | Docker host port for backoffice | diff --git a/apps/backoffice/src/app/features/analytics/pages/api/analytics-api.ts b/apps/backoffice/src/app/features/analytics/pages/api/analytics-api.ts new file mode 100644 index 000000000..67f2a62fe --- /dev/null +++ b/apps/backoffice/src/app/features/analytics/pages/api/analytics-api.ts @@ -0,0 +1,2 @@ +// Analytics API - Coming soon +// test from claude diff --git a/apps/backoffice/src/app/features/analytics/pages/types/analytics.ts b/apps/backoffice/src/app/features/analytics/pages/types/analytics.ts new file mode 100644 index 000000000..ee0d73141 --- /dev/null +++ b/apps/backoffice/src/app/features/analytics/pages/types/analytics.ts @@ -0,0 +1 @@ +// Analytics types - Coming soon diff --git a/apps/backoffice/src/app/features/certificate-designer/pages/CertificateDesignerPage.tsx b/apps/backoffice/src/app/features/certificate-designer/pages/CertificateDesignerPage.tsx index 46b801648..9f47684df 100644 --- a/apps/backoffice/src/app/features/certificate-designer/pages/CertificateDesignerPage.tsx +++ b/apps/backoffice/src/app/features/certificate-designer/pages/CertificateDesignerPage.tsx @@ -39,12 +39,13 @@ import { useGetLicenseTemplatesQuery, useGetLicenseTypesQuery, useGetTemplateVariablesQuery, + useLocalized, usePublishLicenseTemplateMutation, useUpdateLicenseValidityMutation, useUpdateLicenseTemplateMutation, type LicenseTemplate, } from '@ema-platform/api'; -import { EmptyState, ErrorState, PageHeader } from '@ema-platform/ui'; +import { EmptyState, ErrorState, ModalFooter, PageHeader } from '@ema-platform/ui'; import { authStorage, usePermissions } from '@ema-platform/auth'; import { PERMISSIONS } from '../../../layouts/nav-config'; @@ -70,6 +71,7 @@ const STATUS_COLOR: Record = { */ export function CertificateDesignerPage() { const { t } = useTranslation(); + const localized = useLocalized(); const { can } = usePermissions(); const canEdit = can([PERMISSIONS.UPDATE_TEMPLATE]); @@ -226,7 +228,7 @@ export function CertificateDesignerPage() { label={t('designer.licenceType', 'Licence type')} data={(licenseTypes?.items ?? []).map((type) => ({ value: type.id, - label: type.name?.en ?? type.key, + label: localized(type.name) || type.key, }))} value={typeId} onChange={(value) => { @@ -529,7 +531,7 @@ export function CertificateDesignerPage() { 'Starts from the live design, or the built-in layout if this type has none.', )} - + @@ -550,7 +552,7 @@ export function CertificateDesignerPage() { > {t('designer.create', 'Create')} - + diff --git a/apps/backoffice/src/app/features/certification/pages/CertificationPage/actions.tsx b/apps/backoffice/src/app/features/certification/pages/CertificationPage/actions.tsx index ae9db2206..39357fedf 100644 --- a/apps/backoffice/src/app/features/certification/pages/CertificationPage/actions.tsx +++ b/apps/backoffice/src/app/features/certification/pages/CertificationPage/actions.tsx @@ -1,29 +1,29 @@ +import { ActionIcon, Group } from '@mantine/core'; import { IconEdit, IconTrash } from '@tabler/icons-react'; import type { TFunction } from 'i18next'; -import type { AdvancedTableAction } from '@ema-platform/ui'; +import type { AdvancedColumn } from '@ema-platform/ui'; import type { Certification } from '../../types/certification'; -export function certificationColumnActions( +export function certificationActionsColumn( t: TFunction, handlers: { onEdit: (cert: Certification) => void; onDelete: (cert: Certification) => void; }, -): AdvancedTableAction[] { - return [ - { - key: 'edit', - label: t('certification.update'), - color: 'blue', - icon: , - onClick: handlers.onEdit, - }, - { - key: 'delete', - label: t('certification.delete'), - color: 'red', - icon: , - onClick: handlers.onDelete, - }, - ]; +): AdvancedColumn { + return { + header: '', + label: t('certification.columns.actions', 'Actions'), + size: 90, + cell: ({ row }) => ( + + handlers.onEdit(row.original)}> + + + handlers.onDelete(row.original)}> + + + + ), + }; } diff --git a/apps/backoffice/src/app/features/certification/pages/CertificationPage/columns.tsx b/apps/backoffice/src/app/features/certification/pages/CertificationPage/columns.tsx index 2ac23618e..4a56ea6d0 100644 --- a/apps/backoffice/src/app/features/certification/pages/CertificationPage/columns.tsx +++ b/apps/backoffice/src/app/features/certification/pages/CertificationPage/columns.tsx @@ -1,29 +1,26 @@ import { Badge, Text } from '@mantine/core'; import type { TFunction } from 'i18next'; -import type { AdvancedTableColumn } from '@ema-platform/ui'; +import type { AdvancedColumn } from '@ema-platform/ui'; import type { Certification } from '../../types/certification'; export function certificationColumns( t: TFunction, locale: 'en' | 'am', -): AdvancedTableColumn[] { +): AdvancedColumn[] { return [ { - key: 'name', header: t('certification.columns.name'), - render: (cert) => {cert.name[locale]}, + cell: ({ row }) => {row.original.name[locale]}, }, { - key: 'description', header: t('certification.columns.description'), - render: (cert) => {cert.description[locale]}, + cell: ({ row }) => {row.original.description[locale]}, }, { - key: 'status', header: t('certification.columns.status'), - render: (cert) => ( - - {cert.isActive ? t('certification.status.active') : t('certification.status.inactive')} + cell: ({ row }) => ( + + {row.original.isActive ? t('certification.status.active') : t('certification.status.inactive')} ), }, diff --git a/apps/backoffice/src/app/features/certification/pages/CertificationPage/index.tsx b/apps/backoffice/src/app/features/certification/pages/CertificationPage/index.tsx index 35135c6bf..1c5893ab3 100644 --- a/apps/backoffice/src/app/features/certification/pages/CertificationPage/index.tsx +++ b/apps/backoffice/src/app/features/certification/pages/CertificationPage/index.tsx @@ -8,17 +8,13 @@ import { Text, TextInput, Textarea, - Paper, - Loader, - Center, + Card, Alert, } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { useTranslation } from 'react-i18next'; import { IconPlus, IconInfoCircle } from '@tabler/icons-react'; -import { AdvancedTable, notify } from '@ema-platform/ui'; -import { certificationColumns } from './columns'; -import { certificationColumnActions } from './actions'; +import { notify, useErrorHandler, AdvancedTable, useServerTable, ModalFooter } from '@ema-platform/ui'; import { useGetCertificationsQuery, useCreateCertificationMutation, @@ -26,6 +22,8 @@ import { useDeleteCertificationMutation, } from '../../api/certification-api'; import type { Certification } from '../../types/certification'; +import { certificationColumns } from './columns'; +import { certificationActionsColumn } from './actions'; function CertificationForm({ editing, @@ -54,27 +52,29 @@ function CertificationForm({ }; return ( - +
setNameEn(e.currentTarget.value)} size="sm" required /> setNameAm(e.currentTarget.value)} size="sm" required />