diff --git a/docs/architecture.md b/docs/architecture.md index 82b34d2..6a4a21b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -28,9 +28,23 @@ paths: └── src/ ├── app/ # App-level configuration (App.tsx, Providers.tsx) ├── features/ # Feature modules using feature slice architecture - │ ├── auth/ # Authentication feature + │ ├── auth/ # Authentication feature (cross-cutting concern) │ ├── carts/ # Shopping cart feature │ ├── products/ # Product catalog feature + │ └── marketing/ # Feature with sub-feature slices + │ ├── components/ # Marketing-wide components + │ ├── providers/ # Marketing-wide providers + │ ├── models/ # Marketing-wide models + │ ├── rating/ # Sub-feature slice + │ │ ├── components/ + │ │ ├── application/ + │ │ ├── providers/ + │ │ └── models/ + │ └── reviews/ # Sub-feature slice + │ ├── components/ + │ ├── application/ + │ ├── providers/ + │ └── models/ ├── lib/ # Shared libraries and utilities │ ├── api/ # Centralized API layer (queries, mutations, DTOs) │ ├── components/ # Reusable UI components @@ -44,10 +58,6 @@ paths: ## Feature Architecture -Each feature follows feature slice architecture patterns with three layers: - -## Feature Architecture - Each feature follows feature slice architecture patterns with four layers: - **components/** - UI components, presentational and decoupled from business logic (application) and router state. Data access is only through `providers/`. @@ -56,7 +66,7 @@ Each feature follows feature slice architecture patterns with four layers: - files are named after their primary export or reexport: `useCartProductsQuery` → `use-cart-products-query.ts`, `useAddToCartMutation` → `use-add-to-cart-mutation.ts` - **models/** - Domain type definitions only. Exposes frontend models for the feature. When the DTO shape is identical to the domain model, re-export with a domain name (`export type { ProductDto as Product }`). When it diverges, define the domain type here. -**Dependency rule:** +### Dependency rule | Layer | May import from | | -------------- | ------------------------------------------------ | @@ -67,6 +77,14 @@ Each feature follows feature slice architecture patterns with four layers: **Cross-slice primitives:** `features/auth/` and `features/authv2/` are cross-cutting concerns (identity, permissions, auth state). Any feature slice may import from them. +### Sub-feature Slices + +When a feature grows to contain multiple distinct domain sub-areas, each sub-area becomes a **sub-feature slice** — a nested directory with its own four-layer structure (`components/`, `application/`, `providers/`, `models/`). + +The parent feature's layers hold code that is either reusable across sub-feature slices, or too small to warrant its own sub-feature slice. + +The same layer dependency rules apply within sub-feature slices. Additionally, sub-feature layers may import from the parent feature's same or lower layers. Sub-feature slices **may not import from sibling sub-feature slices**. + ## API Library `src/lib/api/` is the global home for all HTTP logic: `queryOptions` factories, loaders, mutation hooks, query keys, domain errors, and DTOs, organised by resource. Query files expose `queryOptions` factories (no `useQuery` hooks — hook composition belongs in `providers/`). Feature `providers/` compose hooks on top of those factories and re-export them for feature slice. New API logic always goes in `src/lib/api/` first, then gets exposed through the relevant feature's `providers/`. diff --git a/eslint.config.mjs b/eslint.config.mjs index 4ee6c46..afd25b1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,79 +10,7 @@ import vitest from "eslint-plugin-vitest"; import reactRefresh from "eslint-plugin-react-refresh"; import reactYouMightNotNeedAnEffect from "eslint-plugin-react-you-might-not-need-an-effect"; -// used by import/no-restricted-paths -// AIDEV-NOTE: import/no-restricted-paths uses path.relative() for matching — glob wildcards -// in target/from are NOT supported. All zone lists must be generated per-feature. -const featureSlices = ["carts", "marketing", "products"]; -const allFeatureSlices = [ - "auth", - "authv2", - "carts", - "demo", - "marketing", - "products", -]; - -// AIDEV-NOTE: `auth` and `authv2` are cross-slice primitives (identity, permissions, -// auth state). Any feature may import from them. See docs/architecture.md -const featureToFeatureZones = featureSlices.map((feature) => ({ - target: `./src/features/${feature}`, - from: "./src/features", - except: [`./${feature}`, "./auth", "./authv2"], - message: "Avoid importing from other features.", -})); - -const featureLayerZones = allFeatureSlices.flatMap((feature) => [ - // application/ ← components/ (forbidden) - { - target: `./src/features/${feature}/application`, - from: `./src/features/${feature}/components`, - message: "application/ must not depend on components/.", - }, - // providers/ ← application/ or components/ (forbidden) - { - target: `./src/features/${feature}/providers`, - from: `./src/features/${feature}/application`, - message: "providers/ must not depend on application/.", - }, - { - target: `./src/features/${feature}/providers`, - from: `./src/features/${feature}/components`, - message: "providers/ must not depend on components/.", - }, - // models/ ← any feature layer (forbidden) - { - target: `./src/features/${feature}/models`, - from: `./src/features/${feature}/application`, - message: "models/ must not depend on application/.", - }, - { - target: `./src/features/${feature}/models`, - from: `./src/features/${feature}/components`, - message: "models/ must not depend on components/.", - }, - { - target: `./src/features/${feature}/models`, - from: `./src/features/${feature}/providers`, - message: "models/ must not depend on providers/.", - }, -]); - -// Prevents lib/api/ from leaking beyond providers/ and models/. -const apiLayerIsolationZones = allFeatureSlices.flatMap((feature) => [ - { - target: `./src/features/${feature}/components`, - from: "./src/lib/api", - message: - "src/lib/api/ must not be imported in components/. Use providers/ for data queries and mutations and models/ for types.", - }, - { - target: `./src/features/${feature}/application`, - from: "./src/lib/api", - message: - "src/lib/api/ must not be imported in application/. Use providers/ for data queries and mutations and models/ for types.", - }, -]); +import { featureSliceConfig } from "./eslint.feature-slices.mjs"; const noAnonymousUseEffectRule = [ "error", @@ -105,19 +33,6 @@ const baseNoRestrictedImports = { ], }; -const reactQueryHooksRestriction = { - name: "@tanstack/react-query", - importNames: [ - "useQuery", - "useMutation", - "useSuspenseQuery", - "useQueries", - "useSuspenseQueries", - "useQueryClient", - ], - message: "React Query hooks belong in providers/, not here.", -}; - const noTestsDirectoryRule = [ "error", { @@ -296,73 +211,10 @@ export default defineConfig( "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-spread": "off", + "@typescript-eslint/no-unsafe-return": "off", }, }, - { - files: ["./src/features/**"], - rules: { - "import/no-restricted-paths": [ - "error", - { - zones: [ - ...featureToFeatureZones, - ...featureLayerZones, - ...apiLayerIsolationZones, - ], - }, - ], - }, - }, - { - files: [ - "./src/features/*/components/**", - "./src/features/*/application/**", - ], - rules: { - "no-restricted-imports": [ - "error", - { - ...baseNoRestrictedImports, - paths: [...baseNoRestrictedImports.paths, reactQueryHooksRestriction], - }, - ], - }, - }, - { - files: ["./src/pages/**"], - rules: { - "import/no-restricted-paths": [ - "error", - { - zones: [ - { - target: "./src/pages", - from: "./src/lib/api", - message: - "src/lib/api/ must not be imported in pages/. Use features/*/providers/ for data and features/*/models/ for types.", - }, - ], - }, - ], - }, - }, - { - files: ["./src/lib/**"], - rules: { - "import/no-restricted-paths": [ - "error", - { - zones: [ - { - target: "./src/lib", - from: "./src/features", - message: "Lib should not depend on features.", - }, - ], - }, - ], - }, - }, + ...featureSliceConfig({ baseNoRestrictedImports }), { files: ["src/**/__tests__/**"], rules: { diff --git a/eslint.feature-slices.mjs b/eslint.feature-slices.mjs new file mode 100644 index 0000000..04ed388 --- /dev/null +++ b/eslint.feature-slices.mjs @@ -0,0 +1,220 @@ +// ESLint rules that enforce feature-slice architecture. +// See docs/architecture.md for the rules this enforces. + +import boundaries from "eslint-plugin-boundaries"; + +const featureLayers = ["components", "application", "providers", "models"]; +const subFeatureLayers = [ + "sub-components", + "sub-application", + "sub-providers", + "sub-models", +]; + +// AIDEV-NOTE: Path patterns map files to element types. Captures (feature, sub) +// flow into rule selectors via {{from.feature}}/{{from.sub}} — they're what +// prevents cross-feature and cross-sub-feature imports without enumerating +// feature lists. Order matters: more specific patterns must come first +// (sub-feature before feature, lib/api before lib). +const elements = [ + { type: "lib-api", pattern: "src/lib/api" }, + { type: "lib", pattern: "src/lib" }, + + { + type: "sub-components", + pattern: "src/features/*/*/components", + capture: ["feature", "sub"], + }, + { + type: "sub-application", + pattern: "src/features/*/*/application", + capture: ["feature", "sub"], + }, + { + type: "sub-providers", + pattern: "src/features/*/*/providers", + capture: ["feature", "sub"], + }, + { + type: "sub-models", + pattern: "src/features/*/*/models", + capture: ["feature", "sub"], + }, + + { + type: "components", + pattern: "src/features/*/components", + capture: ["feature"], + }, + { + type: "application", + pattern: "src/features/*/application", + capture: ["feature"], + }, + { + type: "providers", + pattern: "src/features/*/providers", + capture: ["feature"], + }, + { + type: "models", + pattern: "src/features/*/models", + capture: ["feature"], + }, + + { type: "pages", pattern: "src/pages" }, +]; + +// AIDEV-NOTE: auth/authv2 are cross-slice primitives (identity, permissions, +// auth state). Any feature layer may import from them. +const authCrossCut = { + to: { + type: featureLayers, + captured: { feature: ["auth", "authv2"] }, + }, +}; + +const sameFeature = (type) => ({ + to: { type, captured: { feature: "{{from.feature}}" } }, +}); + +const sameSub = (type) => ({ + to: { + type, + captured: { feature: "{{from.feature}}", sub: "{{from.sub}}" }, + }, +}); + +const typeRules = [ + { + from: { type: "components" }, + allow: [ + sameFeature(["application", "providers", "models"]), + authCrossCut, + { to: { type: "lib" } }, + ], + }, + { + from: { type: "application" }, + allow: [ + sameFeature(["providers", "models"]), + authCrossCut, + { to: { type: "lib" } }, + ], + }, + { + from: { type: "providers" }, + allow: [ + sameFeature("models"), + authCrossCut, + { to: { type: ["lib", "lib-api"] } }, + ], + }, + { + from: { type: "models" }, + allow: [authCrossCut, { to: { type: ["lib", "lib-api"] } }], + }, + + { + from: { type: "sub-components" }, + allow: [ + sameSub(["sub-application", "sub-providers", "sub-models"]), + sameFeature(featureLayers), + authCrossCut, + { to: { type: "lib" } }, + ], + }, + { + from: { type: "sub-application" }, + allow: [ + sameSub(["sub-providers", "sub-models"]), + sameFeature(["application", "providers", "models"]), + authCrossCut, + { to: { type: "lib" } }, + ], + }, + { + from: { type: "sub-providers" }, + allow: [ + sameSub("sub-models"), + sameFeature(["providers", "models"]), + authCrossCut, + { to: { type: ["lib", "lib-api"] } }, + ], + }, + { + from: { type: "sub-models" }, + allow: [ + sameFeature("models"), + authCrossCut, + { to: { type: ["lib", "lib-api"] } }, + ], + }, + + { + from: { type: "pages" }, + allow: [ + { to: { type: [...featureLayers, ...subFeatureLayers, "lib", "pages"] } }, + ], + }, + + { + from: { type: "lib" }, + allow: [{ to: { type: ["lib", "lib-api"] } }], + }, + { + from: { type: "lib-api" }, + allow: [{ to: { type: ["lib", "lib-api"] } }], + }, +]; + +const reactQueryHooksRestriction = { + name: "@tanstack/react-query", + importNames: [ + "useQuery", + "useMutation", + "useSuspenseQuery", + "useQueries", + "useSuspenseQueries", + "useQueryClient", + ], + message: "React Query hooks belong in providers/, not here.", +}; + +export function featureSliceConfig({ baseNoRestrictedImports }) { + return [ + { + files: ["src/**/*.{ts,tsx}"], + plugins: { boundaries }, + settings: { + "boundaries/elements": elements, + }, + rules: { + "boundaries/dependencies": [ + "error", + { default: "disallow", rules: typeRules }, + ], + }, + }, + { + files: [ + "./src/features/*/components/**", + "./src/features/*/application/**", + "./src/features/*/*/components/**", + "./src/features/*/*/application/**", + ], + rules: { + "no-restricted-imports": [ + "error", + { + ...baseNoRestrictedImports, + paths: [ + ...baseNoRestrictedImports.paths, + reactQueryHooksRestriction, + ], + }, + ], + }, + }, + ]; +} diff --git a/package.json b/package.json index 0952f63..2167ed4 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "eslint": "9.39.2", "eslint-config-prettier": "10.1.8", "eslint-import-resolver-typescript": "4.4.4", + "eslint-plugin-boundaries": "6.0.2", "eslint-plugin-import": "2.32.0", "eslint-plugin-prettier": "5.5.4", "eslint-plugin-react": "7.37.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cdb71f..b36c34e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,7 +68,7 @@ importers: specifier: 7.12.0 version: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) use-context-selector: - specifier: ^2.0.0 + specifier: 2.0.0 version: 2.0.0(react@19.2.3)(scheduler@0.27.0) xstate: specifier: 5.25.0 @@ -143,6 +143,9 @@ importers: eslint-import-resolver-typescript: specifier: 4.4.4 version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.2) + eslint-plugin-boundaries: + specifier: ^6.0.2 + version: 6.0.2(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2) eslint-plugin-import: specifier: 2.32.0 version: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2) @@ -498,6 +501,13 @@ packages: integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==, } + "@boundaries/elements@2.0.1": + resolution: + { + integrity: sha512-sAWO3D8PFP6pBXdxxW93SQi/KQqqhE2AAHo3AgWfdtJXwO6bfK6/wUN81XnOZk0qRC6vHzUEKhjwVD9dtDWvxg==, + } + engines: { node: ">=18.18" } + "@chakra-ui/react@3.34.0": resolution: { @@ -3999,6 +4009,15 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-boundaries@6.0.2: + resolution: + { + integrity: sha512-wSHgiYeMEbziP91lH0UQ9oslgF2djG1x+LV9z/qO19ggMKZaCB8pKIGePHAY91eLF4EAgpsxQk8MRSFGRPfPzw==, + } + engines: { node: ">=18.18" } + peerDependencies: + eslint: ">=6.0.0" + eslint-plugin-import@2.32.0: resolution: { @@ -4529,6 +4548,14 @@ packages: } engines: { node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0 } + handlebars@4.7.9: + resolution: + { + integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==, + } + engines: { node: ">=0.4.7" } + hasBin: true + has-bigints@1.0.2: resolution: { @@ -5389,6 +5416,13 @@ packages: } engines: { node: ">=8.6" } + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: ">=8.6" } + mime-db@1.52.0: resolution: { @@ -5533,6 +5567,12 @@ packages: integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, } + neo-async@2.6.2: + resolution: + { + integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, + } + next-themes@0.4.4: resolution: { @@ -6184,13 +6224,6 @@ packages: engines: { node: ">= 0.4" } hasBin: true - resolve@1.22.8: - resolution: - { - integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==, - } - hasBin: true - resolve@2.0.0-next.5: resolution: { @@ -6985,6 +7018,14 @@ packages: engines: { node: ">=14.17" } hasBin: true + uglify-js@3.19.3: + resolution: + { + integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, + } + engines: { node: ">=0.8.0" } + hasBin: true + unbox-primitive@1.1.0: resolution: { @@ -7374,6 +7415,12 @@ packages: } engines: { node: ">=0.10.0" } + wordwrap@1.0.0: + resolution: + { + integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, + } + wrap-ansi@6.2.0: resolution: { @@ -7757,7 +7804,7 @@ snapshots: "@babel/helper-split-export-declaration": 7.24.7 "@babel/parser": 7.26.9 "@babel/types": 7.26.9 - debug: 4.4.0 + debug: 4.4.3 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -7786,6 +7833,20 @@ snapshots: "@blazediff/core@1.9.1": {} + "@boundaries/elements@2.0.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2)": + dependencies: + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2) + handlebars: 4.7.9 + is-core-module: 2.16.1 + micromatch: 4.0.8 + transitivePeerDependencies: + - "@typescript-eslint/parser" + - eslint + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + "@chakra-ui/react@3.34.0(@emotion/react@11.13.0(@types/react@19.2.8)(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)": dependencies: "@ark-ui/react": 5.35.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -8828,7 +8889,7 @@ snapshots: dependencies: "@typescript-eslint/types": 7.18.0 "@typescript-eslint/visitor-keys": 7.18.0 - debug: 4.4.0 + debug: 4.4.3 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -9655,7 +9716,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -9808,7 +9869,7 @@ snapshots: dependencies: "@babel/runtime": 7.24.8 cosmiconfig: 7.1.0 - resolve: 1.22.8 + resolve: 1.22.11 balanced-match@1.0.2: {} @@ -10324,6 +10385,21 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-boundaries@6.0.2(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2): + dependencies: + "@boundaries/elements": 2.0.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2) + chalk: 4.1.2 + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2) + handlebars: 4.7.9 + micromatch: 4.0.8 + transitivePeerDependencies: + - "@typescript-eslint/parser" + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2): dependencies: "@rtsao/scc": 1.1.0 @@ -10716,6 +10792,15 @@ snapshots: graphql@16.12.0: {} + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + has-bigints@1.0.2: {} has-flag@4.0.0: {} @@ -11212,6 +11297,11 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + mime-db@1.52.0: {} mime-types@2.1.35: @@ -11288,6 +11378,8 @@ snapshots: natural-compare@1.4.0: {} + neo-async@2.6.2: {} + next-themes@0.4.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -11657,12 +11749,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.8: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: dependencies: is-core-module: 2.16.1 @@ -12185,6 +12271,9 @@ snapshots: typescript@5.9.3: {} + uglify-js@3.19.3: + optional: true + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -12421,6 +12510,8 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@1.0.0: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 diff --git a/src/features/carts/components/CartItem.tsx b/src/features/carts/components/CartItem.tsx index a0f9055..9ce479b 100644 --- a/src/features/carts/components/CartItem.tsx +++ b/src/features/carts/components/CartItem.tsx @@ -1,4 +1,4 @@ -/* eslint-disable import/no-restricted-paths */ +/* eslint-disable boundaries/dependencies */ import { Box, Text, diff --git a/src/features/marketing/application/use-rate-product-notifications.ts b/src/features/marketing/rating/application/use-rate-product-notifications.ts similarity index 100% rename from src/features/marketing/application/use-rate-product-notifications.ts rename to src/features/marketing/rating/application/use-rate-product-notifications.ts diff --git a/src/features/marketing/application/use-rate-product.ts b/src/features/marketing/rating/application/use-rate-product.ts similarity index 88% rename from src/features/marketing/application/use-rate-product.ts rename to src/features/marketing/rating/application/use-rate-product.ts index 7bf03fc..84585bf 100644 --- a/src/features/marketing/application/use-rate-product.ts +++ b/src/features/marketing/rating/application/use-rate-product.ts @@ -1,7 +1,7 @@ import { useState } from "react"; import { useAuthStore } from "@/features/auth/application/auth-store"; -import { useRateProductMutation } from "@/features/marketing/providers/use-rate-product-mutation"; +import { useRateProductMutation } from "@/features/marketing/rating/providers/use-rate-product-mutation"; import { useRateProductNotifications } from "./use-rate-product-notifications"; diff --git a/src/features/marketing/components/ProductRating.stories.tsx b/src/features/marketing/rating/components/ProductRating.stories.tsx similarity index 100% rename from src/features/marketing/components/ProductRating.stories.tsx rename to src/features/marketing/rating/components/ProductRating.stories.tsx diff --git a/src/features/marketing/components/ProductRating.tsx b/src/features/marketing/rating/components/ProductRating.tsx similarity index 100% rename from src/features/marketing/components/ProductRating.tsx rename to src/features/marketing/rating/components/ProductRating.tsx diff --git a/src/features/marketing/components/ProductRatingFallback.tsx b/src/features/marketing/rating/components/ProductRatingFallback.tsx similarity index 100% rename from src/features/marketing/components/ProductRatingFallback.tsx rename to src/features/marketing/rating/components/ProductRatingFallback.tsx diff --git a/src/features/marketing/components/StarRating.stories.tsx b/src/features/marketing/rating/components/StarRating.stories.tsx similarity index 100% rename from src/features/marketing/components/StarRating.stories.tsx rename to src/features/marketing/rating/components/StarRating.stories.tsx diff --git a/src/features/marketing/components/StarRating.tsx b/src/features/marketing/rating/components/StarRating.tsx similarity index 95% rename from src/features/marketing/components/StarRating.tsx rename to src/features/marketing/rating/components/StarRating.tsx index 8fbe815..e525ffe 100644 --- a/src/features/marketing/components/StarRating.tsx +++ b/src/features/marketing/rating/components/StarRating.tsx @@ -2,7 +2,7 @@ import { HStack, Icon, Portal, Tooltip } from "@chakra-ui/react"; import { Star } from "lucide-react"; import { useState } from "react"; -import { useRateProduct } from "@/features/marketing/application/use-rate-product"; +import { useRateProduct } from "@/features/marketing/rating/application/use-rate-product"; import { useTranslations } from "@/lib/i18n/use-transations"; import { useColorModeValue } from "@/lib/theme/use-color-mode"; diff --git a/src/features/marketing/providers/use-rate-product-mutation.ts b/src/features/marketing/rating/providers/use-rate-product-mutation.ts similarity index 100% rename from src/features/marketing/providers/use-rate-product-mutation.ts rename to src/features/marketing/rating/providers/use-rate-product-mutation.ts diff --git a/src/features/products/components/ProductCard.tsx b/src/features/products/components/ProductCard.tsx index d74274b..edc358a 100644 --- a/src/features/products/components/ProductCard.tsx +++ b/src/features/products/components/ProductCard.tsx @@ -1,4 +1,4 @@ -/* eslint-disable import/no-restricted-paths */ +/* eslint-disable boundaries/dependencies */ import { Box, Text, VStack, HStack } from "@chakra-ui/react"; import { AddToCartButton } from "@/features/carts/components/AddToCartButton/AddToCartButton"; diff --git a/src/features/products/components/ProductDetails.tsx b/src/features/products/components/ProductDetails.tsx index 1b5c6d3..122b065 100644 --- a/src/features/products/components/ProductDetails.tsx +++ b/src/features/products/components/ProductDetails.tsx @@ -1,4 +1,4 @@ -/* eslint-disable import/no-restricted-paths */ +/* eslint-disable boundaries/dependencies */ import { Accordion, Box, diff --git a/src/features/products/components/ProductsList.tsx b/src/features/products/components/ProductsList.tsx index 98d0bee..daa7da4 100644 --- a/src/features/products/components/ProductsList.tsx +++ b/src/features/products/components/ProductsList.tsx @@ -1,4 +1,4 @@ -/* eslint-disable import/no-restricted-paths */ +/* eslint-disable boundaries/dependencies */ import { SimpleGrid, GridItem } from "@chakra-ui/react"; import { ProductAddedDialog } from "@/features/carts/components/AddToCartButton/ProductAddedDialog"; diff --git a/src/lib/components/Layout/Navbar/index.tsx b/src/lib/components/Layout/Navbar/index.tsx index d9431fd..0b766a5 100644 --- a/src/lib/components/Layout/Navbar/index.tsx +++ b/src/lib/components/Layout/Navbar/index.tsx @@ -11,7 +11,7 @@ import { import { Menu, X } from "lucide-react"; import { useState } from "react"; -// eslint-disable-next-line import/no-restricted-paths +// eslint-disable-next-line boundaries/dependencies import { useAuthStore } from "@/features/auth/application/auth-store"; import { useNotImplementedYetToast } from "@/lib/components/Toast/use-not-implemented-yet-toast"; import { Link, useNavigate } from "@/lib/router"; diff --git a/src/lib/components/Layout/Navbar/use-nav-items.ts b/src/lib/components/Layout/Navbar/use-nav-items.ts index da75f0a..057b990 100644 --- a/src/lib/components/Layout/Navbar/use-nav-items.ts +++ b/src/lib/components/Layout/Navbar/use-nav-items.ts @@ -1,4 +1,4 @@ -// eslint-disable-next-line import/no-restricted-paths +// eslint-disable-next-line boundaries/dependencies import { useAuthStore } from "@/features/auth/application/auth-store"; import { generatePath } from "@/lib/router"; import { routes } from "@/lib/router/routes"; diff --git a/src/lib/components/Result/ErrorPageStrategy.tsx b/src/lib/components/Result/ErrorPageStrategy.tsx index 44b28a2..8a94d64 100644 --- a/src/lib/components/Result/ErrorPageStrategy.tsx +++ b/src/lib/components/Result/ErrorPageStrategy.tsx @@ -1,4 +1,4 @@ -// eslint-disable-next-line import/no-restricted-paths +// eslint-disable-next-line boundaries/dependencies import { useAuthStore } from "@/features/auth/application/auth-store"; import { AjaxError } from "@/lib/http/ajax-error"; import { useNavigate, useRouteError } from "@/lib/router"; diff --git a/src/lib/permissions/use-has-permission.ts b/src/lib/permissions/use-has-permission.ts index 446885e..15220f8 100644 --- a/src/lib/permissions/use-has-permission.ts +++ b/src/lib/permissions/use-has-permission.ts @@ -1,4 +1,4 @@ -/* eslint-disable import/no-restricted-paths */ +/* eslint-disable boundaries/dependencies */ import { useAuthorizedContextSelector } from "@/features/authv2/application/use-authorized-context-selector"; import type { Permission } from "@/features/authv2/models/permissions"; diff --git a/src/lib/permissions/when-permitted-to.tsx b/src/lib/permissions/when-permitted-to.tsx index e838574..f8c9162 100644 --- a/src/lib/permissions/when-permitted-to.tsx +++ b/src/lib/permissions/when-permitted-to.tsx @@ -1,6 +1,6 @@ import type { ComponentType } from "react"; -// eslint-disable-next-line import/no-restricted-paths +// eslint-disable-next-line boundaries/dependencies import type { Permission } from "@/features/authv2/models/permissions"; import { useHasPermission } from "@/lib/permissions/use-has-permission"; diff --git a/src/pages/Product/index.tsx b/src/pages/Product/index.tsx index 3bc17bf..000e6e3 100644 --- a/src/pages/Product/index.tsx +++ b/src/pages/Product/index.tsx @@ -1,7 +1,7 @@ import { Button } from "@chakra-ui/react"; import { ArrowLeft } from "lucide-react"; -import { ProductRating } from "@/features/marketing/components/ProductRating"; +import { ProductRating } from "@/features/marketing/rating/components/ProductRating"; import { ProductDetails } from "@/features/products/components/ProductDetails"; import { ProductNotFoundResult } from "@/features/products/components/ProductNotFoundResult"; import { useProductQuery } from "@/features/products/providers/product-query";