Reorganize app workspaces and align Admin architecture
This commit is contained in:
+4
-4
@@ -26,7 +26,7 @@ yarn-error.log
|
||||
# Platform specific
|
||||
.DS_Store
|
||||
|
||||
/backend/public/
|
||||
/backend/storage/
|
||||
/backend/.adonisjs/
|
||||
/backend/database/schema.ts
|
||||
/apps/backend/public/
|
||||
/apps/backend/storage/
|
||||
/apps/backend/.adonisjs/
|
||||
/apps/backend/database/schema.ts
|
||||
|
||||
+3
-3
@@ -3,8 +3,8 @@
|
||||
**/build/**
|
||||
**/dist/**
|
||||
**/coverage/**
|
||||
backend/public/**
|
||||
backend/storage/**
|
||||
apps/backend/public/**
|
||||
apps/backend/storage/**
|
||||
|
||||
# Handlebars generator templates contain application syntax that is not valid HTML.
|
||||
backend/app/templates/**
|
||||
apps/backend/app/templates/**
|
||||
|
||||
@@ -11,8 +11,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
### Changed
|
||||
|
||||
- Moved AdonisJS ESLint and Prettier configuration packages and Japa test dependencies from the
|
||||
root package manifest to the backend workspace so workspace-specific dependencies are owned by
|
||||
`apps/backend`.
|
||||
- Updated the Admin frontend architecture so global providers belong to the `app` composition
|
||||
layer under `app/providers` instead of being modeled as a separate architectural layer.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Admin and Backend Docker startup on ARM64 by removing manually pinned platform-specific
|
||||
optional dependencies and allowing npm to resolve the correct native packages for the container
|
||||
architecture.
|
||||
|
||||
### Removed
|
||||
|
||||
## [1.1.0] - 2026-07-17
|
||||
|
||||
@@ -2,15 +2,13 @@ FROM node:24-alpine3.22
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install workspace deps
|
||||
# Copy all workspaces before installation so npm resolves workspace dependencies.
|
||||
COPY package.json package-lock.json ./
|
||||
COPY apps ./apps
|
||||
COPY packages ./packages
|
||||
|
||||
RUN npm ci --force
|
||||
|
||||
# Copy project
|
||||
COPY apps/admin ./apps/admin
|
||||
COPY packages ./packages
|
||||
|
||||
WORKDIR /app/apps/admin
|
||||
|
||||
ENV CHOKIDAR_USEPOLLING=true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { SidebarHeader } from './SidebarHeader';
|
||||
import { SidebarMenu } from './SidebarMenu';
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { ChevronFirst } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { toAbsoluteUrl } from '#shared/lib/helpers';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
|
||||
export function SidebarHeader() {
|
||||
const { settings, storeOption } = useSettings();
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useEffect } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useEffect, useState } from 'react'; // Add useState to the imports
|
||||
import { useScrollPosition } from '@crux-kit/hooks';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Container } from '#shared/widgets/common/Container';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { HeaderLogo } from './HeaderLogo';
|
||||
import { HeaderTopbar } from './HeaderTopbar';
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import { DateRange } from 'react-day-picker';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
import { useBodyClass } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { Calendar } from '#shared/ui/Calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '#shared/ui/Popover';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Navbar } from './components/Navbar';
|
||||
|
||||
@@ -6,8 +6,8 @@ import { useEffect } from 'react';
|
||||
import { Link, Outlet, useLocation } from 'react-router-dom';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Navbar } from './components/Navbar';
|
||||
|
||||
@@ -6,8 +6,8 @@ import { useEffect } from 'react';
|
||||
import { Link, Outlet } from 'react-router-dom';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
// import { SearchDialog } from '#shared/partials/dialogs/search/search-dialog';
|
||||
// import { NotificationsSheet } from '#shared/partials/topbar/notifications-sheet';
|
||||
import { Footer } from './components/Footer';
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useState } from 'react';
|
||||
import { useScrollPosition } from '@crux-kit/hooks';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Container } from '#shared/widgets/common/Container';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { HeaderLogo } from './HeaderLogo';
|
||||
import { HeaderTopbar } from './HeaderTopbar';
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import { DateRange } from 'react-day-picker';
|
||||
import { Outlet, useLocation } from 'react-router';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { Calendar } from '#shared/ui/Calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '#shared/ui/Popover';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Navbar } from './components/Navbar';
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useState } from 'react';
|
||||
import { useScrollPosition } from '@crux-kit/hooks';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Container } from '#shared/widgets/common/Container';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { HeaderLogo } from './HeaderLogo';
|
||||
import { HeaderTopbar } from './HeaderTopbar';
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import { DateRange } from 'react-day-picker';
|
||||
import { Link, Outlet, useLocation } from 'react-router-dom';
|
||||
import { useBodyClass } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { Calendar } from '#shared/ui/Calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '#shared/ui/Popover';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Toolbar, ToolbarActions, ToolbarHeading } from './components/Toolbar';
|
||||
|
||||
@@ -6,8 +6,8 @@ import { useEffect } from 'react';
|
||||
import { Link, Outlet } from 'react-router-dom';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
// import { SearchDialog } from '#shared/partials/dialogs/search/search-dialog';
|
||||
// import { ChatSheet } from '#shared/partials/topbar/chat-sheet';
|
||||
import { Footer } from './components/Footer';
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useState } from 'react';
|
||||
import { useScrollPosition } from '@crux-kit/hooks';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Container } from '#shared/widgets/common/Container';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { HeaderLogo } from './HeaderLogo';
|
||||
import { HeaderSearch } from './HeaderSearch';
|
||||
import { HeaderTopbar } from './HeaderTopbar';
|
||||
|
||||
@@ -8,10 +8,10 @@ import { DateRange } from 'react-day-picker';
|
||||
import { Link, Outlet, useLocation } from 'react-router';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { Calendar } from '#shared/ui/Calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '#shared/ui/Popover';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Navbar } from './components/Navbar';
|
||||
|
||||
@@ -8,10 +8,10 @@ import { DateRange } from 'react-day-picker';
|
||||
import { Link, Outlet } from 'react-router-dom';
|
||||
import { useBodyClass, useIsMobile } from '@crux-kit/hooks';
|
||||
import { LayoutNames } from '#app/config/types';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { Calendar } from '#shared/ui/Calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '#shared/ui/Popover';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { Footer } from './components/Footer';
|
||||
import { Header } from './components/Header';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { queryClient } from '#providers/QueryProvider';
|
||||
import { queryClient } from '#app/providers/QueryProvider';
|
||||
import { adminDetailedKeyBuilder, adminListKeyBuilder, permissionListKeyBuilder, roleDetailedKeyBuilder, roleListKeyBuilder } from './keys';
|
||||
|
||||
export const invalidateAdminList = async () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import { queryClient } from '#app/providers/QueryProvider';
|
||||
import { AuthApi } from '#core/Auth/data/api/api';
|
||||
import { UpdateUserPayload } from '#core/Auth/data/api/dto';
|
||||
import UserMapper from '#core/Auth/data/mappers/UserMapper';
|
||||
import * as authHelper from '#core/Auth/data/stores/localStorage';
|
||||
import { AuthModel, AuthUser } from '#core/Auth/entities';
|
||||
import { queryClient } from '#providers/QueryProvider';
|
||||
import { AuthContext } from './context';
|
||||
|
||||
export function AuthProvider({ children }: PropsWithChildren) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { InfiniteData, QueryKey } from '@tanstack/react-query';
|
||||
import { queryClient } from '#app/providers/QueryProvider';
|
||||
import { EntryDTO } from '#core/FileManager/data/api/dto';
|
||||
import { AUDIO_EXTENSIONS, getFileExtension, IMAGE_EXTENSIONS, PDF_EXTENSIONS, PLAINTEXT_EXTENSIONS, VIDEO_EXTENSIONS } from '#core/FileManager/shared/file/extension';
|
||||
import { ensureLeadingSlash } from '#core/FileManager/shared/path/leadingSlash';
|
||||
import { queryClient } from '#providers/QueryProvider';
|
||||
import { fetchEntries } from './entries';
|
||||
import { entriesKeyBuilder, EntriesQueryPayload } from './keys';
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function TutorialSection() {
|
||||
Compare frontend permission constants in{' '}
|
||||
<code>apps/admin/src/modules/<name>/config/meta.ts</code> with backend
|
||||
permissions in{' '}
|
||||
<code>backend/modules/<ModuleName>/config/permissions.ts</code>.
|
||||
<code>apps/backend/modules/<ModuleName>/config/permissions.ts</code>.
|
||||
</li>
|
||||
<li>Confirm the current admin role owns the generated view permission.</li>
|
||||
<li>Check that the module manifest exposes its menu and routes.</li>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import { queryClient } from '#app/providers/QueryProvider';
|
||||
import { getAuth, removeAuth } from '#core/Auth/data/stores/localStorage';
|
||||
import { queryClient } from '#providers/QueryProvider';
|
||||
|
||||
export function requestInterceptorSuccess(
|
||||
config: InternalAxiosRequestConfig
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import AppProviders from '#app/providers/AppProviders';
|
||||
import AppRouting from '#app/routing/AppRouting';
|
||||
import AppProviders from '#providers/AppProviders';
|
||||
import './app/css/styles.css';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { queryClient } from '#app/providers/QueryProvider';
|
||||
import { axiosClient } from '#infrastructure/axios';
|
||||
import { queryClient } from '#providers/QueryProvider';
|
||||
import { MODULE_API_ENDPOINT } from '../config/meta';
|
||||
import { Entity } from '../entities/Entity';
|
||||
import { detailedKeyBuilder } from './keys';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { keepPreviousData, useQuery } from '@tanstack/react-query';
|
||||
import React from 'react';
|
||||
import { queryClient } from '#app/providers/QueryProvider';
|
||||
import { axiosClient } from '#infrastructure/axios';
|
||||
import { getPaginationInfo, ListQueryParams, SuccessListResponse } from '#infrastructure/reactQuery';
|
||||
import { queryClient } from '#providers/QueryProvider';
|
||||
import { MODULE_API_ENDPOINT } from '../config/meta';
|
||||
import { Entity } from '../entities/Entity';
|
||||
import { listKeyBuilder } from './keys';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Editor as TinyMCEEditorReact } from '@tinymce/tinymce-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import React, { useRef } from 'react';
|
||||
import { Editor } from 'tinymce';
|
||||
import { useLanguage } from '#providers/I18nProvider';
|
||||
import { useLanguage } from '#app/providers/I18nProvider';
|
||||
import { Statusbar } from './components/Statusbar';
|
||||
import { Toolbar } from './components/Toolbar';
|
||||
import { apiKey, createConfig, defaultToolbarLayout, ToolbarLayout, ToolbarOptions } from './config';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { InputTimeFormat } from '@crux-kit/types';
|
||||
import { useLanguage } from '#app/providers/I18nProvider';
|
||||
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '#shared/ui/form';
|
||||
import { TimeInput } from '#shared/ui/TimeInput';
|
||||
import { useLanguage } from '#providers/I18nProvider';
|
||||
|
||||
export const InputTime: React.FC<{
|
||||
name: string;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ReactNode } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Language } from '#app/i18n/types';
|
||||
import { useLanguage } from '#app/providers/I18nProvider';
|
||||
import { IntlName } from '#infrastructure/IntlName';
|
||||
import { useAuth } from '#core/Auth/providers/AuthProvider';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '#shared/ui/Avatar';
|
||||
@@ -11,7 +12,6 @@ import { Badge } from '#shared/ui/Badge';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '#shared/ui/DropdownMenu';
|
||||
import { Switch } from '#shared/ui/Switch';
|
||||
import { useLanguage } from '#providers/I18nProvider';
|
||||
|
||||
export function UserDropdownMenu({ trigger }: { trigger: ReactNode }) {
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { type ReactNode } from 'react';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { useSettings } from '#providers/SettingsProvider';
|
||||
import { useSettings } from '#app/providers/SettingsProvider';
|
||||
|
||||
const containerVariants = cva('w-full mx-auto px-4 lg:px-6', {
|
||||
variants: {
|
||||
|
||||
@@ -3,17 +3,15 @@ FROM node:24-alpine3.22
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only root workspace files first
|
||||
# Copy all workspaces before installation so npm can resolve their dependencies.
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
# Install workspace dependencies (monorepo-aware)
|
||||
RUN npm ci --force
|
||||
|
||||
# Copy backend source
|
||||
COPY backend ./backend
|
||||
COPY apps ./apps
|
||||
COPY packages ./packages
|
||||
|
||||
WORKDIR /app/backend
|
||||
# Install workspace dependencies (monorepo-aware).
|
||||
RUN npm ci --force
|
||||
|
||||
WORKDIR /app/apps/backend
|
||||
|
||||
# Expose dev port
|
||||
EXPOSE 3333
|
||||
+3
-2
@@ -136,8 +136,9 @@ export default class MakeModuleController {
|
||||
const modulePath = join(appBase, 'modules', moduleName)
|
||||
|
||||
const snake = this.snakeCase(table)
|
||||
const clientBase = join(appBase, '..', 'apps', 'admin', 'src', 'modules', moduleName)
|
||||
const templateModuleDir = join(appBase, '..', 'apps', 'admin', 'src', 'modules', '_template')
|
||||
// Backend and admin are sibling workspaces under apps/.
|
||||
const clientBase = join(appBase, '..', 'admin', 'src', 'modules', moduleName)
|
||||
const templateModuleDir = join(appBase, '..', 'admin', 'src', 'modules', '_template')
|
||||
|
||||
const paths = {
|
||||
// backend
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user