├── apps
├── auth
│ ├── src
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── workflows
│ │ │ └── index.ts
│ │ ├── config
│ │ │ ├── swagger.config.ts
│ │ │ ├── temporal.config.ts
│ │ │ ├── app.config.ts
│ │ │ └── env.config.ts
│ │ ├── app
│ │ │ ├── user
│ │ │ │ ├── user.module.ts
│ │ │ │ ├── user.service.ts
│ │ │ │ ├── user.controller.spec.ts
│ │ │ │ ├── user.service.spec.ts
│ │ │ │ └── user.controller.ts
│ │ │ ├── activities
│ │ │ │ ├── activities.module.ts
│ │ │ │ └── activities.service.ts
│ │ │ ├── app.service.spec.ts
│ │ │ └── app.controller.spec.ts
│ │ └── main.ts
│ ├── eslint.config.js
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.app.json
│ ├── jest.config.ts
│ ├── webpack.config.js
│ └── project.json
├── order
│ ├── src
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── workflows
│ │ │ ├── index.ts
│ │ │ └── long-running.workflow.ts
│ │ ├── config
│ │ │ ├── swagger.config.ts
│ │ │ ├── temporal.config.ts
│ │ │ ├── app.config.ts
│ │ │ └── env.config.ts
│ │ ├── app
│ │ │ ├── order
│ │ │ │ ├── order.module.ts
│ │ │ │ └── order.service.spec.ts
│ │ │ ├── activities
│ │ │ │ ├── activities.module.ts
│ │ │ │ └── activities.service.ts
│ │ │ ├── app.service.spec.ts
│ │ │ └── app.controller.spec.ts
│ │ └── main.ts
│ ├── eslint.config.js
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── jest.config.ts
│ ├── tsconfig.app.json
│ ├── webpack.config.js
│ └── project.json
├── product
│ ├── src
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── config
│ │ │ ├── swagger.config.ts
│ │ │ ├── app.config.ts
│ │ │ └── env.config.ts
│ │ ├── main.ts
│ │ └── app
│ │ │ ├── app.service.spec.ts
│ │ │ ├── app.service.ts
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.module.ts
│ │ │ └── app.controller.ts
│ ├── eslint.config.js
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── jest.config.ts
│ ├── tsconfig.app.json
│ ├── webpack.config.js
│ └── project.json
├── web
│ ├── .gitignore
│ ├── app
│ │ ├── providers
│ │ │ ├── workflows
│ │ │ │ ├── services
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── order.ts
│ │ │ │ ├── store
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── types.ts
│ │ │ │ │ └── store.tsx
│ │ │ │ ├── types
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── order.ts
│ │ │ │ │ ├── events.ts
│ │ │ │ │ └── workflow.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── utils.ts
│ │ │ │ ├── useCurrentWorkflow.ts
│ │ │ │ └── useWorkflows.ts
│ │ │ ├── auth
│ │ │ │ ├── index.ts
│ │ │ │ ├── hoc.tsx
│ │ │ │ ├── hook.ts
│ │ │ │ └── context.tsx
│ │ │ ├── index.ts
│ │ │ └── cart.tsx
│ │ ├── constants
│ │ │ ├── theme.ts
│ │ │ ├── index.ts
│ │ │ ├── links.ts
│ │ │ └── navigation.ts
│ │ ├── routes
│ │ │ ├── logout.tsx
│ │ │ ├── $.tsx
│ │ │ ├── _index.tsx
│ │ │ ├── product.tsx
│ │ │ ├── email.$template.tsx
│ │ │ ├── order-summary.tsx
│ │ │ ├── order-history.tsx
│ │ │ ├── order-detail.tsx
│ │ │ ├── profile.tsx
│ │ │ ├── admin.tsx
│ │ │ └── marketplace.tsx
│ │ ├── services
│ │ │ └── logger.server.ts
│ │ ├── pages
│ │ │ ├── checkout
│ │ │ │ ├── Successful.tsx
│ │ │ │ ├── Failure.tsx
│ │ │ │ └── Pending.tsx
│ │ │ ├── PageLayout.tsx
│ │ │ └── HomePage.tsx
│ │ ├── tailwind.css
│ │ ├── config
│ │ │ ├── env.server.ts
│ │ │ ├── utils.server.ts
│ │ │ └── app.config.server.ts
│ │ ├── hooks
│ │ │ └── useProducts.ts
│ │ └── cookies
│ │ │ └── session.server.ts
│ ├── public
│ │ └── favicon.ico
│ ├── test-setup.ts
│ ├── eslint.config.cjs
│ ├── remix.env.d.ts
│ ├── project.json
│ ├── tailwind.config.ts
│ ├── tests
│ │ └── routes
│ │ │ └── _index.spec.tsx
│ ├── remix.config.js
│ ├── package.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.app.json
│ ├── vitest.config.ts
│ └── tsconfig.json
├── auth-e2e
│ ├── eslint.config.js
│ ├── tsconfig.spec.json
│ ├── tsconfig.json
│ ├── src
│ │ ├── support
│ │ │ ├── global-teardown.ts
│ │ │ ├── test-setup.ts
│ │ │ └── global-setup.ts
│ │ └── auth
│ │ │ └── auth.spec.ts
│ ├── project.json
│ └── jest.config.ts
├── order-e2e
│ ├── eslint.config.js
│ ├── tsconfig.spec.json
│ ├── tsconfig.json
│ ├── src
│ │ ├── support
│ │ │ ├── global-teardown.ts
│ │ │ ├── test-setup.ts
│ │ │ └── global-setup.ts
│ │ └── order
│ │ │ └── order.spec.ts
│ ├── project.json
│ └── jest.config.ts
└── product-e2e
│ ├── eslint.config.js
│ ├── tsconfig.spec.json
│ ├── tsconfig.json
│ ├── src
│ ├── support
│ │ ├── global-teardown.ts
│ │ ├── test-setup.ts
│ │ └── global-setup.ts
│ └── product
│ │ └── product.spec.ts
│ ├── project.json
│ └── jest.config.ts
├── libs
├── models
│ ├── src
│ │ ├── role
│ │ │ ├── user-role.dto.ts
│ │ │ ├── create-role.dto.ts
│ │ │ ├── update-role.dto.ts
│ │ │ └── role.dto.ts
│ │ ├── validators
│ │ │ ├── index.ts
│ │ │ └── profanity.ts
│ │ ├── manufacturer
│ │ │ └── index.ts
│ │ ├── auth
│ │ │ ├── index.ts
│ │ │ ├── login.dto.ts
│ │ │ └── auth.dto.ts
│ │ ├── order
│ │ │ ├── index.ts
│ │ │ └── update-order.dto.ts
│ │ ├── user
│ │ │ ├── index.ts
│ │ │ ├── update-user.dto.ts
│ │ │ └── create-user.dto.ts
│ │ ├── product
│ │ │ ├── index.ts
│ │ │ ├── update-product.dto.ts
│ │ │ └── create-product.dto.ts
│ │ ├── enums.ts
│ │ ├── index.ts
│ │ └── transforms.ts
│ ├── eslint.config.js
│ ├── README.md
│ ├── project.json
│ ├── jest.config.ts
│ ├── tsconfig.spec.json
│ ├── tsconfig.json
│ └── tsconfig.lib.json
├── backend
│ ├── workflows
│ │ ├── src
│ │ │ ├── index.ts
│ │ │ └── lib
│ │ │ │ ├── constants.ts
│ │ │ │ ├── utils.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── worker.ts
│ │ │ │ └── workflows.module.ts
│ │ ├── eslint.config.js
│ │ ├── README.md
│ │ ├── project.json
│ │ ├── tsconfig.spec.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.lib.json
│ │ └── tsconfig.json
│ ├── core
│ │ ├── src
│ │ │ ├── lib
│ │ │ │ ├── logger
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── logger.ts
│ │ │ │ ├── nest
│ │ │ │ │ └── index.ts
│ │ │ │ ├── order
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── providers.ts
│ │ │ │ ├── security
│ │ │ │ │ └── index.ts
│ │ │ │ ├── swagger
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── setup.ts
│ │ │ │ ├── health
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── health.module.ts
│ │ │ │ ├── configuration
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── validation.ts
│ │ │ │ ├── user
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── user.interface.ts
│ │ │ │ │ ├── user.decorator.ts
│ │ │ │ │ └── workflow.utils.ts
│ │ │ │ ├── auth
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── auth.guard.ts
│ │ │ │ │ ├── auth.service.ts
│ │ │ │ │ ├── auth.module.ts
│ │ │ │ │ └── jwt.strategy.ts
│ │ │ │ ├── hashing.ts
│ │ │ │ ├── workflows
│ │ │ │ │ └── index.ts
│ │ │ │ └── core.module.ts
│ │ │ └── index.ts
│ │ ├── eslint.config.js
│ │ ├── README.md
│ │ ├── types
│ │ │ └── index.d.ts
│ │ ├── project.json
│ │ ├── tsconfig.spec.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.lib.json
│ │ └── tsconfig.json
│ ├── db
│ │ ├── .gitignore
│ │ ├── src
│ │ │ ├── lib
│ │ │ │ ├── user
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── user-schema.module.ts
│ │ │ │ ├── order
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── order-schema.module.ts
│ │ │ │ ├── product
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── product-schema.module.ts
│ │ │ │ ├── prisma.module.ts
│ │ │ │ ├── db.module.ts
│ │ │ │ └── prisma.service.ts
│ │ │ └── index.ts
│ │ ├── eslint.config.js
│ │ ├── prisma
│ │ │ └── migrations
│ │ │ │ └── migration_lock.toml
│ │ ├── project.json
│ │ ├── tsconfig.spec.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.lib.json
│ │ └── tsconfig.json
│ ├── email
│ │ ├── eslint.config.js
│ │ ├── src
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ ├── common
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── utils.ts
│ │ │ │ │ ├── mjml.ts
│ │ │ │ │ ├── header.ts
│ │ │ │ │ ├── head.ts
│ │ │ │ │ └── constants.ts
│ │ │ │ ├── email.module.ts
│ │ │ │ ├── order
│ │ │ │ │ ├── orderPending.ts
│ │ │ │ │ ├── orderFailed.ts
│ │ │ │ │ └── orderSuccess.ts
│ │ │ │ ├── template.ts
│ │ │ │ ├── auth
│ │ │ │ │ └── login.ts
│ │ │ │ └── factory.ts
│ │ │ └── config
│ │ │ │ └── email.config.ts
│ │ ├── README.md
│ │ ├── project.json
│ │ ├── tsconfig.spec.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.lib.json
│ │ └── tsconfig.json
│ └── payment
│ │ ├── eslint.config.js
│ │ ├── src
│ │ ├── config
│ │ │ ├── index.ts
│ │ │ └── payment.config.ts
│ │ ├── index.ts
│ │ └── lib
│ │ │ └── payment.module.ts
│ │ ├── README.md
│ │ ├── project.json
│ │ ├── tsconfig.spec.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.lib.json
│ │ └── tsconfig.json
└── frontend
│ └── ui
│ ├── src
│ ├── lib
│ │ ├── layouts
│ │ │ └── index.ts
│ │ ├── navigation
│ │ │ ├── index.ts
│ │ │ ├── Navigation.stories.tsx
│ │ │ └── MobileNavigation.stories.tsx
│ │ ├── drawers
│ │ │ ├── index.ts
│ │ │ └── ShoppingCartDrawer.tsx
│ │ ├── footer
│ │ │ ├── Footer.spec.tsx
│ │ │ └── Footer.stories.tsx
│ │ ├── header
│ │ │ ├── Header.spec.tsx
│ │ │ └── Header.stories.tsx
│ │ ├── inputs
│ │ │ └── search
│ │ │ │ ├── Search.spec.tsx
│ │ │ │ └── Search.stories.tsx
│ │ ├── buttons
│ │ │ ├── button
│ │ │ │ ├── Button.spec.tsx
│ │ │ │ ├── Button.stories.tsx
│ │ │ │ └── Button.tsx
│ │ │ ├── theme
│ │ │ │ ├── ThemeButton.spec.tsx
│ │ │ │ ├── ThemeButton.stories.tsx
│ │ │ │ └── ThemeButton.tsx
│ │ │ └── checkout
│ │ │ │ ├── CheckoutButton.spec.tsx
│ │ │ │ └── CheckoutButton.tsx
│ │ ├── icons
│ │ │ ├── Mic.stories.tsx
│ │ │ ├── Love.stories.tsx
│ │ │ ├── Menu.stories.tsx
│ │ │ ├── Close.stories.tsx
│ │ │ ├── GitHub.stories.tsx
│ │ │ ├── Search.stories.tsx
│ │ │ ├── Close.tsx
│ │ │ ├── Facebook.tsx
│ │ │ ├── Menu.tsx
│ │ │ ├── Facebook.stories.tsx
│ │ │ ├── Instagram.stories.tsx
│ │ │ ├── Love.tsx
│ │ │ ├── Instagram.tsx
│ │ │ ├── Search.tsx
│ │ │ ├── Mic.tsx
│ │ │ └── GitHub.tsx
│ │ └── providers
│ │ │ └── router.tsx
│ ├── utils
│ │ ├── index.ts
│ │ ├── dom.ts
│ │ ├── tailwind.ts
│ │ └── cookie.ts
│ ├── hooks
│ │ ├── useAvatarUrl.ts
│ │ └── useScroll.ts
│ └── index.ts
│ ├── .storybook
│ ├── preview.ts
│ ├── tailwind-imports.css
│ └── main.ts
│ ├── styles
│ └── index.css
│ ├── README.md
│ ├── .babelrc
│ ├── eslint.config.js
│ ├── tsconfig.tailwind.json
│ ├── tsconfig.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── tsconfig.lib.json
│ └── tsconfig.storybook.json
├── .vscode
├── settings.json
└── extensions.json
├── vitest.workspace.ts
├── jest.preset.js
├── .prettierignore
├── docker-entrypoint-initdb.d
└── init-extensions.sql
├── .prettierrc
├── jest.config.ts
├── .editorconfig
├── .dockerignore
├── Dockerfile
├── deployment
├── scripts
│ └── search-attributes.sh
└── config
│ └── dynamicconfig
│ └── development.yaml
├── .gitignore
├── eslint.config.js
├── tsconfig.base.json
├── .github
└── workflows
│ └── ci.yml
└── prompts
└── thinking.md
/apps/auth/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/order/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/product/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/models/src/role/user-role.dto.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/backend/workflows/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib';
2 |
--------------------------------------------------------------------------------
/apps/web/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | build
3 | public/build
4 | .env
5 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/layouts/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Layout';
--------------------------------------------------------------------------------
/libs/models/src/validators/index.ts:
--------------------------------------------------------------------------------
1 | export * from './profanity';
2 |
--------------------------------------------------------------------------------
/apps/auth/src/workflows/index.ts:
--------------------------------------------------------------------------------
1 | export * from './login.workflow';
2 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/logger/index.ts:
--------------------------------------------------------------------------------
1 | export * from './logger';
2 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/nest/index.ts:
--------------------------------------------------------------------------------
1 | export * from './bootstrap';
2 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/order/index.ts:
--------------------------------------------------------------------------------
1 | export * from './workflow.utils';
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/security/index.ts:
--------------------------------------------------------------------------------
1 | export * from './setup';
2 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/swagger/index.ts:
--------------------------------------------------------------------------------
1 | export * from './setup';
2 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Navigation';
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/health/index.ts:
--------------------------------------------------------------------------------
1 | export * from './health.module';
2 |
--------------------------------------------------------------------------------
/libs/models/src/manufacturer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './manufacturer.dto';
2 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './order';
2 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/configuration/index.ts:
--------------------------------------------------------------------------------
1 | export * from './validation';
2 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/drawers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './ShoppingCartDrawer';
2 |
--------------------------------------------------------------------------------
/apps/web/app/providers/auth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './hoc';
2 | export * from './hook';
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "nxConsole.nxWorkspacePath": "${workspaceFolder}/nx.json"
3 | }
--------------------------------------------------------------------------------
/libs/backend/workflows/src/lib/constants.ts:
--------------------------------------------------------------------------------
1 | export const WORKER_OPTIONS_TOKEN = 'WORKER_OPTIONS';
--------------------------------------------------------------------------------
/libs/models/src/auth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './login.dto';
2 | export * from './auth.dto';
3 |
--------------------------------------------------------------------------------
/vitest.workspace.ts:
--------------------------------------------------------------------------------
1 | export default ['**/*/vite.config.{ts,mts}', '**/*/vitest.config.{ts,mts}'];
2 |
--------------------------------------------------------------------------------
/apps/web/app/constants/theme.ts:
--------------------------------------------------------------------------------
1 | export enum THEME {
2 | DARK = 'dark',
3 | LIGHT = 'light',
4 | }
5 |
--------------------------------------------------------------------------------
/libs/backend/db/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | # Keep environment variables out of version control
3 | .env
4 |
--------------------------------------------------------------------------------
/libs/frontend/ui/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import './tailwind-imports.css';
2 | import '../styles/index.css';
--------------------------------------------------------------------------------
/apps/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proyecto26/projectx/HEAD/apps/web/public/favicon.ico
--------------------------------------------------------------------------------
/jest.preset.js:
--------------------------------------------------------------------------------
1 | const nxPreset = require('@nx/jest/preset').default;
2 |
3 | module.exports = { ...nxPreset };
4 |
--------------------------------------------------------------------------------
/apps/order/src/workflows/index.ts:
--------------------------------------------------------------------------------
1 | export * from './order.workflow';
2 | export * from './process-payment.workflow';
3 |
--------------------------------------------------------------------------------
/apps/web/app/constants/index.ts:
--------------------------------------------------------------------------------
1 | export * from './links';
2 | export * from './navigation';
3 | export * from './theme';
4 |
--------------------------------------------------------------------------------
/libs/backend/workflows/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
2 |
--------------------------------------------------------------------------------
/apps/auth/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/apps/order/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/user/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user-repository.service';
2 | export * from './user-schema.module';
3 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './tailwind';
2 | export * from './dom';
3 | export * from './cookie';
4 |
--------------------------------------------------------------------------------
/apps/auth-e2e/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/apps/order-e2e/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/apps/product/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/order/index.ts:
--------------------------------------------------------------------------------
1 | export * from './order-repository.service';
2 | export * from './order-schema.module';
3 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/product/index.ts:
--------------------------------------------------------------------------------
1 | export * from './product-repository.service';
2 | export * from './product-schema.module';
--------------------------------------------------------------------------------
/libs/models/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 | /dist
3 | /coverage
4 | /.nx/cache
5 | /.nx/workspace-data
--------------------------------------------------------------------------------
/apps/product-e2e/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/store/index.ts:
--------------------------------------------------------------------------------
1 | export * from './reducer';
2 | export * from './store';
3 | export * from './types';
4 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './workflow';
2 | export * from './events';
3 | export * from './order';
4 |
--------------------------------------------------------------------------------
/docker-entrypoint-initdb.d/init-extensions.sql:
--------------------------------------------------------------------------------
1 | CREATE EXTENSION IF NOT EXISTS pg_trgm;
2 | CREATE EXTENSION IF NOT EXISTS postgis;
3 |
--------------------------------------------------------------------------------
/libs/backend/core/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/libs/backend/db/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "plugins": ["prettier-plugin-tailwindcss"]
6 | }
7 |
--------------------------------------------------------------------------------
/libs/backend/email/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/libs/backend/payment/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/libs/backend/payment/src/config/index.ts:
--------------------------------------------------------------------------------
1 | export { default as paymentConfig } from './payment.config';
2 | export * from './payment.config';
--------------------------------------------------------------------------------
/libs/backend/workflows/eslint.config.js:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig];
4 |
--------------------------------------------------------------------------------
/libs/frontend/ui/.storybook/tailwind-imports.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | @tailwind screens;
5 |
--------------------------------------------------------------------------------
/libs/models/src/order/index.ts:
--------------------------------------------------------------------------------
1 | export * from './create-order.dto';
2 | export * from './order.dto';
3 | export * from './update-order.dto';
4 |
--------------------------------------------------------------------------------
/libs/models/src/user/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user.dto';
2 | export * from './create-user.dto';
3 | export * from './update-user.dto';
4 |
--------------------------------------------------------------------------------
/apps/web/test-setup.ts:
--------------------------------------------------------------------------------
1 | import { installGlobals } from '@remix-run/node';
2 | import '@testing-library/jest-dom/matchers';
3 | installGlobals();
4 |
--------------------------------------------------------------------------------
/libs/backend/email/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/template';
2 | export * from './lib/email.module';
3 | export * from './lib/email.service';
4 |
--------------------------------------------------------------------------------
/apps/web/app/providers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './cart';
2 | export * from './query';
3 | export * from './auth';
4 | export * from './workflows';
5 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/user/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user.decorator';
2 | export * from './user.interface';
3 | export * from './workflow.utils';
4 |
--------------------------------------------------------------------------------
/libs/models/src/product/index.ts:
--------------------------------------------------------------------------------
1 | export * from './product.dto';
2 | export * from './create-product.dto';
3 | export * from './update-product.dto';
4 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/constants.ts:
--------------------------------------------------------------------------------
1 | // HTTP STATUS GONE
2 | export const EXPIRED_STATUS_CODE = 410;
3 | export const NOT_FOUND_STATUS_CODE = 404;
--------------------------------------------------------------------------------
/libs/backend/payment/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/payment.module';
2 | export * from './lib/stripe/stripe.service';
3 | export * from './config';
4 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { getJestProjectsAsync } from '@nx/jest';
2 |
3 | export default async () => ({
4 | projects: await getJestProjectsAsync(),
5 | });
6 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/utils/dom.ts:
--------------------------------------------------------------------------------
1 | export const canUseDOM =
2 | typeof window !== 'undefined' &&
3 | window.document &&
4 | window.document.createElement;
5 |
--------------------------------------------------------------------------------
/apps/web/eslint.config.cjs:
--------------------------------------------------------------------------------
1 | const baseConfig = require('../../eslint.config.js');
2 |
3 | module.exports = [...baseConfig, { ignores: ['build', 'public/build'] }];
4 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/user/user.interface.ts:
--------------------------------------------------------------------------------
1 | export interface AuthUser {
2 | id: number;
3 | username: string;
4 | email: string;
5 | roles: string[];
6 | }
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "nrwl.angular-console",
4 | "esbenp.prettier-vscode",
5 | "firsttris.vscode-jest-runner"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/libs/models/src/enums.ts:
--------------------------------------------------------------------------------
1 | export enum Environment {
2 | Development = "development",
3 | Production = "production",
4 | Test = "test",
5 | Provision = "provision",
6 | }
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/auth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth.guard';
2 | export * from './auth.module';
3 | export * from './auth.service';
4 | export * from './jwt.strategy';
5 |
--------------------------------------------------------------------------------
/libs/backend/db/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "postgresql"
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/common/index.ts:
--------------------------------------------------------------------------------
1 | export * from './constants';
2 | export * from './head';
3 | export * from './header';
4 | export * from './footer';
5 | export * from './mjml';
6 |
--------------------------------------------------------------------------------
/apps/web/remix.env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | ///
5 |
--------------------------------------------------------------------------------
/libs/frontend/ui/styles/index.css:
--------------------------------------------------------------------------------
1 | @import 'react-toastify/dist/ReactToastify.css';
2 |
3 | @import './base.css';
4 | @import './dark.css';
5 |
6 |
7 | @view-transition: {
8 | navigation: auto;
9 | }
--------------------------------------------------------------------------------
/libs/models/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './enums';
2 | export * from './user';
3 | export * from './auth';
4 | export * from './manufacturer';
5 | export * from './product';
6 | export * from './order';
7 |
--------------------------------------------------------------------------------
/libs/frontend/ui/README.md:
--------------------------------------------------------------------------------
1 | # ui
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test ui` to execute the unit tests via [Vitest](https://vitest.dev/).
8 |
--------------------------------------------------------------------------------
/libs/models/README.md:
--------------------------------------------------------------------------------
1 | # models
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test models` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/common/utils.ts:
--------------------------------------------------------------------------------
1 | // Add any utility functions here that can be reused across templates
2 | export function formatCurrency(amount: number): string {
3 | return `$${amount.toFixed(2)}`;
4 | }
5 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/auth/auth.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { AuthGuard } from '@nestjs/passport';
3 |
4 | @Injectable()
5 | export class JwtAuthGuard extends AuthGuard('jwt') {}
6 |
--------------------------------------------------------------------------------
/libs/backend/payment/README.md:
--------------------------------------------------------------------------------
1 | # payment
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test payment` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/backend/workflows/README.md:
--------------------------------------------------------------------------------
1 | # workflows
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test workflows` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/backend/core/README.md:
--------------------------------------------------------------------------------
1 | # ProjectX Core Library
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test core` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/backend/email/README.md:
--------------------------------------------------------------------------------
1 | # ProjectX Email Library
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test email` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/apps/web/app/constants/links.ts:
--------------------------------------------------------------------------------
1 | export const GITHUB_URL = 'https://github.com/proyecto26';
2 | export const FACEBOOK_URL = 'https://www.facebook.com/proyecto26';
3 | export const INSTAGRAM_URL = 'https://www.instagram.com/proyecto_26';
4 |
--------------------------------------------------------------------------------
/libs/backend/db/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/db.module';
2 | export * from './lib/prisma.module';
3 | export * from './lib/prisma.service';
4 | export * from './lib/user';
5 | export * from './lib/order';
6 | export * from './lib/product';
7 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useWorkflows';
2 | export * from './useWorkflowActions';
3 | export * from './useCurrentWorkflow';
4 | export * from './services';
5 | export * from './types';
6 | export * from './store';
7 |
--------------------------------------------------------------------------------
/libs/backend/workflows/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | export * from './worker';
2 | export * from './constants';
3 | export * from './client.service';
4 | export * from './worker.service';
5 | export * from './workflow.utils';
6 | export * from './workflows.module';
--------------------------------------------------------------------------------
/apps/auth/src/config/swagger.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('swagger', () => ({
4 | title: 'Auth API',
5 | description: 'Provides the endpoints for authentication',
6 | version: '1.0',
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/order/src/config/swagger.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('swagger', () => ({
4 | title: 'Order API',
5 | description: 'Provides the endpoints for order management',
6 | version: '1.0',
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/product/src/config/swagger.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('swagger', () => ({
4 | title: 'Product API',
5 | description: 'Provides the endpoints for product management',
6 | version: '1.0',
7 | }));
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/prisma.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PrismaService } from './prisma.service';
3 |
4 | @Module({
5 | providers: [PrismaService],
6 | exports: [PrismaService],
7 | })
8 | export class PrismaModule {}
9 |
--------------------------------------------------------------------------------
/apps/web/app/routes/logout.tsx:
--------------------------------------------------------------------------------
1 | import { LoaderFunction } from '@remix-run/node';
2 |
3 | import { logoutRedirect } from '~/cookies/auth.server';
4 |
5 | export const loader: LoaderFunction = async ({ request }) => {
6 | throw await logoutRedirect(request);
7 | };
8 |
--------------------------------------------------------------------------------
/libs/models/src/role/create-role.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType } from '@nestjs/swagger';
2 |
3 | import { RoleDto } from './role.dto';
4 |
5 | export class CreateRoleDto extends OmitType(RoleDto, [
6 | 'id',
7 | 'createdAt',
8 | 'updatedAt',
9 | ] as const) {}
10 |
--------------------------------------------------------------------------------
/libs/models/src/role/update-role.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType, PartialType } from '@nestjs/swagger';
2 |
3 | import { RoleDto } from './role.dto';
4 |
5 | export class UpdateRoleDto extends PartialType(
6 | OmitType(RoleDto, ['createdAt', 'updatedAt'] as const)
7 | ) {}
8 |
--------------------------------------------------------------------------------
/apps/auth-e2e/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": ["jest.config.ts", "src/**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/auth/src/config/temporal.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('temporal', () => ({
4 | host: process.env.TEMPORAL_HOST,
5 | namespace: process.env.TEMPORAL_NAMESPACE || 'default',
6 | taskQueue: 'auth',
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/order-e2e/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": ["jest.config.ts", "src/**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/order/src/config/temporal.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('temporal', () => ({
4 | host: process.env.TEMPORAL_HOST,
5 | namespace: process.env.TEMPORAL_NAMESPACE || 'default',
6 | taskQueue: 'order',
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/product-e2e/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": ["jest.config.ts", "src/**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/libs/models/src/user/update-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType, PartialType } from '@nestjs/swagger';
2 |
3 | import { UserDto } from './user.dto';
4 |
5 | export class UpdateUserDto extends PartialType(
6 | OmitType(UserDto, ['createdAt', 'updatedAt', 'deletedAt'] as const)
7 | ) {}
8 |
--------------------------------------------------------------------------------
/libs/models/src/product/update-product.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType, PartialType } from '@nestjs/swagger';
2 |
3 | import { ProductDto } from './product.dto';
4 |
5 | export class UpdateProductDto extends PartialType(
6 | OmitType(ProductDto, ['createdAt', 'updatedAt'] as const)
7 | ) {}
8 |
--------------------------------------------------------------------------------
/apps/auth-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.spec.json"
8 | }
9 | ],
10 | "compilerOptions": {
11 | "esModuleInterop": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/order-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.spec.json"
8 | }
9 | ],
10 | "compilerOptions": {
11 | "esModuleInterop": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/libs/models/src/product/create-product.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType } from '@nestjs/swagger';
2 |
3 | import { ProductDto } from './product.dto';
4 |
5 | export class CreateProductDto extends OmitType(ProductDto, [
6 | 'id',
7 | 'createdAt',
8 | 'updatedAt',
9 | ] as const) {}
10 |
--------------------------------------------------------------------------------
/libs/models/src/user/create-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType } from '@nestjs/swagger';
2 |
3 | import { UserDto } from './user.dto';
4 |
5 | export class CreateUserDto extends OmitType(UserDto, [
6 | 'id',
7 | 'createdAt',
8 | 'updatedAt',
9 | 'deletedAt',
10 | ] as const) {}
11 |
--------------------------------------------------------------------------------
/apps/product-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.spec.json"
8 | }
9 | ],
10 | "compilerOptions": {
11 | "esModuleInterop": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/web/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/web",
5 | "projectType": "application",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project web --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/libs/backend/email/src/config/email.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from "@nestjs/config";
2 |
3 | export default registerAs('email', () => ({
4 | apiKey: process.env['SENDGRID_API_KEY'] ?? '',
5 | fromEmail: process.env['EMAIL_FROM'] ?? '',
6 | fromName: process.env['EMAIL_FROM_NAME'] ?? '',
7 | }));
8 |
--------------------------------------------------------------------------------
/libs/frontend/ui/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nx/react/babel",
5 | {
6 | "runtime": "automatic",
7 | "useBuiltIns": "usage"
8 | }
9 | ],
10 | "@babel/preset-react",
11 | "@babel/preset-typescript"
12 | ],
13 | "plugins": []
14 | }
15 |
--------------------------------------------------------------------------------
/apps/auth-e2e/src/support/global-teardown.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | module.exports = async function () {
4 | // Put clean up logic here (e.g. stopping services, docker-compose, etc.).
5 | // Hint: `globalThis` is shared between setup and teardown.
6 | console.log(globalThis.__TEARDOWN_MESSAGE__);
7 | };
8 |
--------------------------------------------------------------------------------
/apps/order-e2e/src/support/global-teardown.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | module.exports = async function () {
4 | // Put clean up logic here (e.g. stopping services, docker-compose, etc.).
5 | // Hint: `globalThis` is shared between setup and teardown.
6 | console.log(globalThis.__TEARDOWN_MESSAGE__);
7 | };
8 |
--------------------------------------------------------------------------------
/apps/product-e2e/src/support/global-teardown.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | module.exports = async function () {
4 | // Put clean up logic here (e.g. stopping services, docker-compose, etc.).
5 | // Hint: `globalThis` is shared between setup and teardown.
6 | console.log(globalThis.__TEARDOWN_MESSAGE__);
7 | };
8 |
--------------------------------------------------------------------------------
/apps/web/app/services/logger.server.ts:
--------------------------------------------------------------------------------
1 | import { createLoggerOptions } from '@projectx/core/logger';
2 | import pino from 'pino';
3 |
4 | import { environment } from '~/config/app.config.server';
5 |
6 | const loggerOptions = createLoggerOptions('info', 'web', environment);
7 | export const logger = pino(loggerOptions);
8 |
--------------------------------------------------------------------------------
/libs/backend/core/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { AuthUser } from '../src/lib/user/user.interface';
2 |
3 | declare namespace Express {
4 | export interface Request {
5 | user?: AuthUser;
6 | }
7 | }
8 |
9 | declare module 'express' {
10 | export interface Request {
11 | user?: AuthUser;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/libs/backend/db/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "db",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/backend/db/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project db --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/libs/models/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "models",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/models/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project models --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/apps/auth-e2e/src/auth/auth.spec.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | describe('GET /api', () => {
4 | it('should return a message', async () => {
5 | const res = await axios.get(`/api`);
6 |
7 | expect(res.status).toBe(200);
8 | expect(res.data).toEqual({ message: 'Hello API' });
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/order/providers.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Enum representing supported payment providers.
3 | *
4 | * @enum {string}
5 | */
6 | export enum PaymentProvider {
7 | Stripe = 'Stripe',
8 | MercadoPago = 'MercadoPago',
9 | PayU = 'PayU',
10 | Wompi = 'Wompi',
11 | // Add more providers as needed
12 | }
--------------------------------------------------------------------------------
/apps/order-e2e/src/order/order.spec.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | describe('GET /api', () => {
4 | it('should return a message', async () => {
5 | const res = await axios.get(`/api`);
6 |
7 | expect(res.status).toBe(200);
8 | expect(res.data).toEqual({ message: 'Hello API' });
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/libs/backend/core/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "core",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/backend/core/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project core --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/apps/product-e2e/src/product/product.spec.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | describe('GET /api', () => {
4 | it('should return a message', async () => {
5 | const res = await axios.get(`/api`);
6 |
7 | expect(res.status).toBe(200);
8 | expect(res.data).toEqual({ message: 'Hello API' });
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/libs/backend/email/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "email",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/backend/email/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project email --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/apps/product/src/main.ts:
--------------------------------------------------------------------------------
1 | import { bootstrapApp } from '@projectx/core';
2 | import { Logger } from '@nestjs/common';
3 |
4 | import { AppModule } from './app/app.module';
5 |
6 | bootstrapApp(AppModule).catch((err) => {
7 | Logger.error(
8 | `⚠️ Application failed to start: ${err}`
9 | )
10 | process.exit(1);
11 | });
12 |
--------------------------------------------------------------------------------
/libs/backend/payment/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "payment",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/backend/payment/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project payment --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/hooks/useAvatarUrl.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import md5 from 'md5';
3 |
4 | export const useAvatarUrl = (email: string) => {
5 | return useMemo(() => {
6 | return email
7 | ? `https://gravatar.com/avatar/${md5(email)}?s=400&d=robohash&r=x`
8 | : '';
9 | }, [email]);
10 | };
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/footer/Footer.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import Footer from './Footer';
4 |
5 | describe('Footer', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/header/Header.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import Header from './Header';
4 |
5 | describe('Header', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/inputs/search/Search.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import Search from './Search';
4 |
5 | describe('Search', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/apps/auth/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ],
13 | "compilerOptions": {
14 | "esModuleInterop": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/order/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ],
13 | "compilerOptions": {
14 | "esModuleInterop": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/libs/backend/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/core.module';
2 | export * from './lib/hashing';
3 | export * from './lib/logger';
4 | export * from './lib/configuration';
5 | export * from './lib/nest';
6 | export * from './lib/user';
7 | export * from './lib/auth';
8 | export * from './lib/order';
9 | export * from './lib/workflows';
10 |
--------------------------------------------------------------------------------
/libs/backend/workflows/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "workflows",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/backend/workflows/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "// targets": "to see all targets run: nx show project workflows --web",
8 | "targets": {}
9 | }
10 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/button/Button.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import Button from './Button';
4 |
5 | describe('Button', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/apps/auth-e2e/src/support/test-setup.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import axios from 'axios';
4 |
5 | module.exports = async function () {
6 | // Configure axios for tests to use.
7 | const host = process.env.HOST ?? 'localhost';
8 | const port = process.env.PORT ?? '3000';
9 | axios.defaults.baseURL = `http://${host}:${port}`;
10 | };
11 |
--------------------------------------------------------------------------------
/apps/product/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ],
13 | "compilerOptions": {
14 | "esModuleInterop": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/auth/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/apps/order-e2e/src/support/test-setup.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import axios from 'axios';
4 |
5 | module.exports = async function () {
6 | // Configure axios for tests to use.
7 | const host = process.env.HOST ?? 'localhost';
8 | const port = process.env.PORT ?? '3000';
9 | axios.defaults.baseURL = `http://${host}:${port}`;
10 | };
11 |
--------------------------------------------------------------------------------
/apps/order/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/apps/product-e2e/src/support/test-setup.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import axios from 'axios';
4 |
5 | module.exports = async function () {
6 | // Configure axios for tests to use.
7 | const host = process.env.HOST ?? 'localhost';
8 | const port = process.env.PORT ?? '3000';
9 | axios.defaults.baseURL = `http://${host}:${port}`;
10 | };
11 |
--------------------------------------------------------------------------------
/apps/product/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/apps/web/app/constants/navigation.ts:
--------------------------------------------------------------------------------
1 | export const NAVIGATION = [
2 | {
3 | title: 'Welcome',
4 | links: [
5 | { title: 'Home', href: '/' },
6 | ],
7 | },
8 | {
9 | title: 'About Us',
10 | links: [
11 | { title: 'Privacy', href: '/privacy' },
12 | { title: 'Terms of use', href: '/terms' },
13 | ],
14 | },
15 | ];
--------------------------------------------------------------------------------
/apps/web/app/pages/checkout/Successful.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const SuccessPage: React.FC = () => {
4 | return (
5 |
6 |
¡Pago Exitoso!
7 |
Gracias por tu compra.
8 |
9 | );
10 | };
11 |
12 | export default SuccessPage;
--------------------------------------------------------------------------------
/libs/backend/db/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/apps/order/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'order',
3 | preset: '../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../coverage/apps/order',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/backend/core/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/libs/backend/email/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/libs/backend/payment/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/theme/ThemeButton.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import ThemeButton from './ThemeButton';
4 |
5 | describe('ThemeButton', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/libs/models/src/order/update-order.dto.ts:
--------------------------------------------------------------------------------
1 | import { OmitType, PartialType } from '@nestjs/swagger';
2 |
3 | import { OrderDto } from './order.dto';
4 |
5 | export class UpdateOrderDto extends PartialType(
6 | OmitType(OrderDto, [
7 | 'userId',
8 | 'status',
9 | 'totalPrice',
10 | 'createdAt',
11 | 'updatedAt',
12 | ] as const)
13 | ) {}
14 |
--------------------------------------------------------------------------------
/apps/product/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'product',
3 | preset: '../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../coverage/apps/product',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/backend/workflows/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/libs/frontend/ui/eslint.config.js:
--------------------------------------------------------------------------------
1 | const nx = require('@nx/eslint-plugin');
2 | const baseConfig = require('../../../eslint.config.js');
3 |
4 | module.exports = [
5 | ...baseConfig,
6 | ...nx.configs['flat/react'],
7 | {
8 | files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
9 | // Override or add rules here
10 | rules: {},
11 | },
12 | ];
13 |
--------------------------------------------------------------------------------
/libs/models/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'models',
3 | preset: '../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../coverage/libs/models',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/footer/Footer.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { Footer } from './Footer';
3 |
4 | const meta: Meta = {
5 | component: Footer,
6 | title: 'Footer',
7 | };
8 | export default meta;
9 | type Story = StoryObj;
10 |
11 | export const Primary = {
12 | args: {},
13 | };
14 |
--------------------------------------------------------------------------------
/apps/auth/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["node"],
7 | "emitDecoratorMetadata": true,
8 | "target": "es2021"
9 | },
10 | "include": ["src/**/*.ts"],
11 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
12 | }
--------------------------------------------------------------------------------
/libs/backend/db/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'db',
3 | preset: '../../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../../coverage/libs/backend/db',
10 | };
11 |
--------------------------------------------------------------------------------
/apps/order/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["node"],
7 | "emitDecoratorMetadata": true,
8 | "target": "es2021"
9 | },
10 | "include": ["src/**/*.ts"],
11 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/libs/backend/core/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'core',
3 | preset: '../../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../../coverage/libs/backend/core',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/backend/email/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'email',
3 | preset: '../../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../../coverage/libs/backend/email',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/checkout/CheckoutButton.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import CheckoutButton from './CheckoutButton';
4 |
5 | describe('CheckoutButton', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/apps/product/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["node"],
7 | "emitDecoratorMetadata": true,
8 | "target": "es2021"
9 | },
10 | "include": ["src/**/*.ts"],
11 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/web/app/pages/checkout/Failure.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const FailurePage: React.FC = () => {
4 | return (
5 |
6 |
Pago Fallido
7 |
Hubo un problema con tu pago. Por favor, intenta nuevamente.
8 |
9 | );
10 | };
11 |
12 | export default FailurePage;
--------------------------------------------------------------------------------
/libs/frontend/ui/src/utils/tailwind.ts:
--------------------------------------------------------------------------------
1 | import clsx, { ClassValue } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export const replaceNewLinesWithSpaces = (str: string) => {
5 | return str.replace(/\s{2,}/g, ' ').trim();
6 | };
7 |
8 | export const classnames = (...inputs: ClassValue[]) => {
9 | return twMerge(replaceNewLinesWithSpaces(clsx(inputs)));
10 | };
11 |
--------------------------------------------------------------------------------
/apps/web/app/pages/checkout/Pending.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const PendingPage: React.FC = () => {
4 | return (
5 |
6 |
Pago Pendiente
7 |
Tu pago está pendiente. Te notificaremos una vez se complete.
8 |
9 | );
10 | };
11 |
12 | export default PendingPage;
--------------------------------------------------------------------------------
/apps/web/app/routes/$.tsx:
--------------------------------------------------------------------------------
1 | import { json, LoaderFunction } from '@remix-run/node';
2 | import PageLayout from '~/pages/PageLayout';
3 |
4 | export const loader: LoaderFunction = () => {
5 | return json(null, { status: 404 });
6 | };
7 |
8 | export default function NotFoundPage() {
9 | return (
10 |
11 | Not Found
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/libs/backend/payment/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'payment',
3 | preset: '../../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../../coverage/libs/backend/payment',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/tsconfig.tailwind.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist",
5 | "target": "es2019",
6 | "types": ["node"],
7 | "moduleResolution": "Node",
8 | "skipLibCheck": true,
9 | "skipDefaultLibCheck": true
10 | },
11 | "include": ["tailwind.config.ts"],
12 | "exclude": ["node_modules", "dist"]
13 | }
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/user/user-schema.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 |
3 | import { UserRepositoryService } from './user-repository.service';
4 | import { PrismaModule } from '../prisma.module';
5 |
6 | @Module({
7 | imports: [PrismaModule],
8 | exports: [UserRepositoryService],
9 | providers: [UserRepositoryService],
10 | })
11 | export class UserSchemaModule {}
12 |
--------------------------------------------------------------------------------
/libs/backend/workflows/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'workflows',
3 | preset: '../../../jest.preset.js',
4 | testEnvironment: 'node',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../../coverage/libs/backend/workflows',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/utils/cookie.ts:
--------------------------------------------------------------------------------
1 | export function getCookie(name: string) {
2 | const value = `; ${document.cookie}`;
3 | const parts = value.split(`; ${name}=`);
4 | if (parts.length === 2) return parts.pop()?.split(';').shift();
5 | }
6 |
7 | export function saveCookie(cookieName: string, value: unknown) {
8 | document.cookie = `${cookieName}=${value};path=/;max-age=31536000`;
9 | }
10 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/order/order-schema.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 |
3 | import { OrderRepositoryService } from './order-repository.service';
4 | import { PrismaModule } from '../prisma.module';
5 |
6 | @Module({
7 | imports: [PrismaModule],
8 | exports: [OrderRepositoryService],
9 | providers: [OrderRepositoryService],
10 | })
11 | export class OrderSchemaModule {}
12 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/buttons/checkout/CheckoutButton';
2 | export * from './lib/buttons/theme/ThemeButton';
3 | export * from './lib/buttons/button/Button';
4 | export * from './lib/footer/Footer';
5 | export * from './lib/header/Header';
6 | export * from './lib/navigation';
7 | export * from './lib/layouts';
8 | export * from './lib/drawers';
9 | export * from './utils';
10 |
--------------------------------------------------------------------------------
/libs/models/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"],
7 | "composite": true,
8 | "noEmit": false
9 | },
10 | "include": [
11 | "jest.config.ts",
12 | "src/**/*.test.ts",
13 | "src/**/*.spec.ts",
14 | "src/**/*.d.ts"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/product/product-schema.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 |
3 | import { ProductRepositoryService } from './product-repository.service';
4 | import { PrismaModule } from '../prisma.module';
5 |
6 | @Module({
7 | imports: [PrismaModule],
8 | exports: [ProductRepositoryService],
9 | providers: [ProductRepositoryService],
10 | })
11 | export class ProductSchemaModule {}
--------------------------------------------------------------------------------
/apps/auth/src/config/app.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('app', () => ({
4 | port: Number(process.env.AUTH_PORT) || 8081,
5 | environment: process.env.NODE_ENV,
6 | apiPrefix: 'auth',
7 | allowedOrigins: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
8 | logLevel: process.env.LOG_LEVEL ?? 'info',
9 | jwtSecret: process.env.JWT_SECRET,
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/order/src/config/app.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('app', () => ({
4 | port: Number(process.env.ORDER_PORT) || 8082,
5 | environment: process.env.NODE_ENV,
6 | apiPrefix: 'order',
7 | allowedOrigins: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
8 | logLevel: process.env.LOG_LEVEL ?? 'info',
9 | jwtSecret: process.env.JWT_SECRET,
10 | }));
11 |
--------------------------------------------------------------------------------
/libs/backend/payment/src/config/payment.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export interface PaymentConfig {
4 | stripeSecretKey: string;
5 | stripeWebhookSecret: string;
6 | }
7 |
8 | export default registerAs('payment', (): PaymentConfig => ({
9 | stripeSecretKey: process.env['STRIPE_SECRET_KEY'] ?? '',
10 | stripeWebhookSecret: process.env['STRIPE_WEBHOOK_SECRET'] ?? '',
11 | }));
--------------------------------------------------------------------------------
/apps/order/src/app/order/order.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { OrderSchemaModule } from '@projectx/db';
3 | import { PaymentModule } from '@projectx/payment';
4 |
5 | import { OrderService } from './order.service';
6 |
7 | @Module({
8 | imports: [PaymentModule, OrderSchemaModule],
9 | providers: [OrderService],
10 | exports: [OrderService],
11 | })
12 | export class OrderModule {}
13 |
--------------------------------------------------------------------------------
/apps/product/src/config/app.config.ts:
--------------------------------------------------------------------------------
1 | import { registerAs } from '@nestjs/config';
2 |
3 | export default registerAs('app', () => ({
4 | port: Number(process.env.PRODUCT_PORT) || 8083,
5 | environment: process.env.NODE_ENV,
6 | apiPrefix: 'product',
7 | allowedOrigins: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
8 | logLevel: process.env.LOG_LEVEL ?? 'info',
9 | jwtSecret: process.env.JWT_SECRET,
10 | }));
--------------------------------------------------------------------------------
/apps/auth-e2e/src/support/global-setup.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var __TEARDOWN_MESSAGE__: string;
3 |
4 | module.exports = async function () {
5 | // Start services that that the app needs to run (e.g. database, docker-compose, etc.).
6 | console.log('\nSetting up...\n');
7 |
8 | // Hint: Use `globalThis` to pass variables to global teardown.
9 | globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n';
10 | };
11 |
--------------------------------------------------------------------------------
/apps/order-e2e/src/support/global-setup.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var __TEARDOWN_MESSAGE__: string;
3 |
4 | module.exports = async function () {
5 | // Start services that that the app needs to run (e.g. database, docker-compose, etc.).
6 | console.log('\nSetting up...\n');
7 |
8 | // Hint: Use `globalThis` to pass variables to global teardown.
9 | globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n';
10 | };
11 |
--------------------------------------------------------------------------------
/apps/product-e2e/src/support/global-setup.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var __TEARDOWN_MESSAGE__: string;
3 |
4 | module.exports = async function () {
5 | // Start services that that the app needs to run (e.g. database, docker-compose, etc.).
6 | console.log('\nSetting up...\n');
7 |
8 | // Hint: Use `globalThis` to pass variables to global teardown.
9 | globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n';
10 | };
11 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/db.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 |
3 | import { UserSchemaModule } from './user';
4 | import { OrderSchemaModule } from './order';
5 | import { ProductSchemaModule } from './product';
6 |
7 | const providers = [UserSchemaModule, OrderSchemaModule, ProductSchemaModule];
8 |
9 | @Module({
10 | imports: providers,
11 | exports: providers,
12 | })
13 | export class DbModule {}
14 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/email.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule } from '@nestjs/config';
3 |
4 | import emailConfig from '../config/email.config';
5 | import { EmailService } from './email.service';
6 |
7 | @Module({
8 | imports: [ConfigModule.forFeature(emailConfig)],
9 | providers: [EmailService],
10 | exports: [EmailService],
11 | })
12 | export class EmailModule {}
13 |
--------------------------------------------------------------------------------
/apps/web/app/tailwind.css:
--------------------------------------------------------------------------------
1 | @import '../../../libs/frontend/ui/styles/index.css';
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 |
8 | input[type="number"]::-webkit-outer-spin-button,
9 | input[type="number"]::-webkit-inner-spin-button {
10 | -webkit-appearance: none;
11 | margin: 0;
12 | -webkit-appearance: none;
13 | }
14 | input[type="number"] {
15 | -moz-appearance: textfield;
16 | }
17 |
--------------------------------------------------------------------------------
/apps/auth/src/app/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { UserSchemaModule } from '@projectx/db';
3 |
4 | import { UserController } from './user.controller';
5 | import { UserService } from './user.service';
6 |
7 | @Module({
8 | imports: [UserSchemaModule],
9 | controllers: [UserController],
10 | providers: [UserService],
11 | exports: [UserService],
12 | })
13 | export class UserModule {}
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
15 | [*.tsx]
16 | max_line_length = 80
17 | trim_trailing_whitespace = false
18 | indent_size = 2
19 | indent_style = space
20 |
--------------------------------------------------------------------------------
/apps/auth/src/app/activities/activities.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { EmailModule } from '@projectx/email';
3 |
4 | import { UserModule } from '../user/user.module';
5 | import { ActivitiesService } from './activities.service';
6 |
7 | @Module({
8 | imports: [EmailModule, UserModule],
9 | providers: [ActivitiesService],
10 | exports: [ActivitiesService],
11 | })
12 | export class ActivitiesModule {}
13 |
--------------------------------------------------------------------------------
/apps/auth/src/main.ts:
--------------------------------------------------------------------------------
1 | import { bootstrapApp } from '@projectx/core';
2 | import { Logger } from '@nestjs/common';
3 |
4 | import { AppModule } from './app/app.module';
5 |
6 | // Export activities to be used in workflows
7 | export * from './app/activities/activities.service';
8 |
9 | bootstrapApp(AppModule).catch((err) => {
10 | Logger.error(
11 | `⚠️ Application failed to start: ${err}`
12 | )
13 | process.exit(1);
14 | });
15 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/hashing.ts:
--------------------------------------------------------------------------------
1 | import { genSalt, hash, compare } from 'bcrypt';
2 |
3 | const SALT_ROUNDS = 10;
4 |
5 | export const hashValue = async (value: string): Promise => {
6 | const salt = await genSalt(SALT_ROUNDS);
7 | return hash(value, salt);
8 | };
9 |
10 | export const compareValue = async (
11 | value: string,
12 | hash: string
13 | ): Promise => {
14 | return compare(value, hash);
15 | };
16 |
--------------------------------------------------------------------------------
/apps/order/src/app/activities/activities.module.ts:
--------------------------------------------------------------------------------
1 | import { HttpModule } from '@nestjs/axios';
2 | import { Module } from '@nestjs/common';
3 |
4 | import { ActivitiesService } from './activities.service';
5 | import { OrderModule } from '../order/order.module';
6 |
7 | @Module({
8 | imports: [HttpModule, OrderModule],
9 | providers: [ActivitiesService],
10 | exports: [ActivitiesService],
11 | })
12 | export class ActivitiesModule {}
13 |
--------------------------------------------------------------------------------
/libs/backend/payment/src/lib/payment.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule } from '@nestjs/config';
3 |
4 | import { StripeService } from './stripe/stripe.service';
5 | import { paymentConfig } from '../config';
6 |
7 | @Module({
8 | imports: [
9 | ConfigModule.forFeature(paymentConfig)
10 | ],
11 | providers: [StripeService],
12 | exports: [StripeService],
13 | })
14 | export class PaymentModule {}
15 |
--------------------------------------------------------------------------------
/libs/backend/db/src/lib/prisma.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2 | import { PrismaClient } from '@prisma/client';
3 |
4 | @Injectable()
5 | export class PrismaService
6 | extends PrismaClient
7 | implements OnModuleInit, OnModuleDestroy
8 | {
9 | async onModuleInit() {
10 | await this.$connect();
11 | }
12 |
13 | async onModuleDestroy() {
14 | await this.$disconnect();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/web/app/routes/_index.tsx:
--------------------------------------------------------------------------------
1 | import { redirect } from '@remix-run/node';
2 |
3 | import HomePage from '~/pages/HomePage';
4 | import PageLayout from '~/pages/PageLayout';
5 |
6 | export const loader = () => {
7 | // Set default route to marketplace
8 | throw redirect('/marketplace');
9 | };
10 |
11 | export default function Index() {
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/apps/web/app/providers/auth/hoc.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentType } from "react";
2 | import { AuthContextType, AuthProvider } from "./context";
3 |
4 | export function withAuthProvider(
5 | WrappedComponent: ComponentType,
6 | ): ComponentType {
7 | return function (props: T & AuthContextType) {
8 | return (
9 |
10 |
11 |
12 | );
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/apps/order/src/workflows/long-running.workflow.ts:
--------------------------------------------------------------------------------
1 | import { continueAsNew, workflowInfo } from "@temporalio/workflow";
2 |
3 | const MAX_NUMBER_OF_EVENTS = 10000;
4 | // It's just an example of a long running workflow
5 | export async function longRunningWorkflow(n: number): Promise {
6 | // Long-duration workflow
7 | while (workflowInfo().historyLength < MAX_NUMBER_OF_EVENTS) {
8 | //...
9 | }
10 |
11 | await continueAsNew(n + 1);
12 | }
13 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Node modules and build artifacts
2 | node_modules
3 | dist
4 | build
5 | .cache
6 |
7 | # Nx cache and temporary files
8 | .nx
9 | tmp
10 |
11 | # Version control and configuration files
12 | .git
13 | .gitignore
14 | Dockerfile
15 | .dockerignore
16 |
17 | # Logs and error files
18 | npm-debug.log
19 | yarn-error.log
20 |
21 | # Editor and OS-specific files
22 | *.swp
23 | *.swo
24 | .DS_Store
25 | Thumbs.db
26 |
27 | # Ignore repo files
28 | libs/frontend
29 | apps/web
--------------------------------------------------------------------------------
/apps/web/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @nx/enforce-module-boundaries
2 | import uiTailwindConfig from '../../libs/frontend/ui/tailwind.config.ts';
3 | import { createGlobPatternsForDependencies } from '@nx/react/tailwind';
4 | import type { Config } from 'tailwindcss';
5 |
6 | export default {
7 | content: [
8 | './app/**/*.{js,jsx,ts,tsx}',
9 | ...createGlobPatternsForDependencies(__dirname),
10 | ],
11 | presets: [uiTailwindConfig],
12 | } satisfies Config;
13 |
--------------------------------------------------------------------------------
/apps/web/tests/routes/_index.spec.tsx:
--------------------------------------------------------------------------------
1 | import { createRemixStub } from '@remix-run/testing';
2 | import { render, screen, waitFor } from '@testing-library/react';
3 | import Index from '../../app/routes/_index';
4 |
5 | test('renders loader data', async () => {
6 | const RemixStub = createRemixStub([
7 | {
8 | path: '/',
9 | Component: Index,
10 | },
11 | ]);
12 |
13 | render();
14 |
15 | await waitFor(() => screen.findByText('Hello there,'));
16 | });
17 |
--------------------------------------------------------------------------------
/libs/frontend/ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "allowJs": false,
5 | "esModuleInterop": false,
6 | "allowSyntheticDefaultImports": true,
7 | "strict": true,
8 | "types": [
9 | "vite/client"
10 | ],
11 | "moduleResolution": "Bundler"
12 | },
13 | "files": [],
14 | "include": [],
15 | "references": [
16 | {
17 | "path": "./tsconfig.lib.json"
18 | }
19 | ],
20 | "extends": "../../../tsconfig.base.json"
21 | }
--------------------------------------------------------------------------------
/apps/order/src/main.ts:
--------------------------------------------------------------------------------
1 | import { Logger } from '@nestjs/common';
2 | import { bootstrapApp } from '@projectx/core';
3 |
4 | import { AppModule } from './app/app.module';
5 |
6 | // Export activities to be used in workflows
7 | export * from './app/activities/activities.service';
8 |
9 | // Enable raw body parsing for webhook events
10 | bootstrapApp(AppModule, { rawBody: true }).catch((err) => {
11 | Logger.error(
12 | `⚠️ Application failed to start: ${err}`
13 | )
14 | process.exit(1);
15 | });
16 |
17 |
--------------------------------------------------------------------------------
/apps/auth/src/app/app.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test } from '@nestjs/testing';
2 |
3 | import { AppService } from './app.service';
4 |
5 | describe('AppService', () => {
6 | let service: AppService;
7 |
8 | beforeAll(async () => {
9 | const app = await Test.createTestingModule({
10 | providers: [AppService],
11 | }).compile();
12 |
13 | service = app.get(AppService);
14 | });
15 |
16 | it('should be defined', () => {
17 | expect(service).toBeDefined();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/web/app/providers/cart.tsx:
--------------------------------------------------------------------------------
1 | import React, { ComponentType, PropsWithChildren } from 'react';
2 | import { CartProvider } from 'react-use-cart';
3 |
4 | const CART_ID = 'shopping-cart';
5 |
6 | export function withCartProvider(
7 | WrappedComponent: ComponentType,
8 | ): ComponentType {
9 | return function (props: T) {
10 | return (
11 |
12 |
13 |
14 | );
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/order/orderPending.ts:
--------------------------------------------------------------------------------
1 | import { getEmailHtml } from '../common/mjml';
2 |
3 | export type OrderPendingEmailData = {
4 | orderId: string;
5 | };
6 |
7 | export function getOrderPendingEmailHtml(
8 | data: T
9 | ): string {
10 | const body = `
11 |
12 |
13 | Your order ${data.orderId} is pending.
14 |
15 |
16 | `;
17 | return getEmailHtml(body);
18 | }
19 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/order/orderFailed.ts:
--------------------------------------------------------------------------------
1 | import { getEmailHtml } from '../common/mjml';
2 |
3 | export type OrderFailedEmailData = {
4 | orderId: string;
5 | };
6 |
7 | export function getOrderFailedEmailHtml(
8 | data: T
9 | ): string {
10 | const body = `
11 |
12 |
13 | Unfortunately, your order ${data.orderId} failed.
14 |
15 |
16 | `;
17 | return getEmailHtml(body);
18 | }
19 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/order/orderSuccess.ts:
--------------------------------------------------------------------------------
1 | import { getEmailHtml } from '../common/mjml';
2 |
3 | export type OrderSuccessEmailData = {
4 | orderId: string;
5 | };
6 |
7 | export function getOrderSuccessEmailHtml(
8 | data: T
9 | ): string {
10 | const body = `
11 |
12 |
13 | Your order ${data.orderId} was successful!
14 |
15 |
16 | `;
17 | return getEmailHtml(body);
18 | }
19 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/template.ts:
--------------------------------------------------------------------------------
1 | import { EmailTemplateDataMap, EmailTemplateFactory } from './factory';
2 |
3 | export enum EmailTemplates {
4 | AuthLogin = 'auth_login',
5 | OrderPending = 'order_pending',
6 | OrderSuccess = 'order_success',
7 | OrderFailed = 'order_failed',
8 | }
9 |
10 | export function getEmailTemplate(
11 | templateKey: EmailTemplates,
12 | data: EmailTemplateDataMap[T]
13 | ) {
14 | return EmailTemplateFactory.createEmailTemplate(templateKey, data);
15 | }
16 |
--------------------------------------------------------------------------------
/apps/auth/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from '@jest/types';
2 |
3 | const config: Config.InitialOptions = {
4 | displayName: 'auth',
5 | preset: '../../jest.preset.js',
6 | globals: {
7 | 'ts-jest': {
8 | tsconfig: '/tsconfig.spec.json',
9 | },
10 | },
11 | coverageDirectory: '../../coverage/apps/auth',
12 | transform: {
13 | '^.+\\.[tj]s$': 'ts-jest',
14 | },
15 | moduleFileExtensions: ['ts', 'js', 'html'],
16 | testEnvironment: 'node',
17 | };
18 |
19 | export default config;
20 |
--------------------------------------------------------------------------------
/apps/product/src/config/env.config.ts:
--------------------------------------------------------------------------------
1 | import { Environment } from '@projectx/models';
2 | import {
3 | IsDefined,
4 | IsEnum,
5 | IsNotEmpty,
6 | IsInt,
7 | IsString,
8 | Max,
9 | Min,
10 | } from 'class-validator';
11 |
12 | export class EnvironmentVariables {
13 | @IsEnum(Environment)
14 | @IsDefined()
15 | NODE_ENV: Environment;
16 |
17 | @IsInt()
18 | @Min(0)
19 | @Max(65535)
20 | @IsDefined()
21 | PRODUCT_PORT: number;
22 |
23 | @IsString()
24 | @IsNotEmpty()
25 | JWT_SECRET: string;
26 | }
27 |
--------------------------------------------------------------------------------
/apps/auth-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth-e2e",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "implicitDependencies": ["auth"],
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/jest:jest",
9 | "outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"],
10 | "options": {
11 | "jestConfig": "apps/auth-e2e/jest.config.ts",
12 | "passWithNoTests": true
13 | },
14 | "dependsOn": ["auth:build"]
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/order-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "order-e2e",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "implicitDependencies": ["order"],
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/jest:jest",
9 | "outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"],
10 | "options": {
11 | "jestConfig": "apps/order-e2e/jest.config.ts",
12 | "passWithNoTests": true
13 | },
14 | "dependsOn": ["order:build"]
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/libs/backend/core/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"],
7 | "target": "es2021",
8 | "strictNullChecks": true,
9 | "noImplicitAny": true,
10 | "strictBindCallApply": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true
13 | },
14 | "include": ["src/**/*.ts"],
15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/libs/backend/db/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"],
7 | "target": "es2021",
8 | "strictNullChecks": true,
9 | "noImplicitAny": true,
10 | "strictBindCallApply": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true
13 | },
14 | "include": ["src/**/*.ts"],
15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/hooks/useScroll.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | export function useScroll () {
4 | const [isScrolled, setIsScrolled] = useState(false);
5 |
6 | useEffect(() => {
7 | function onScroll() {
8 | setIsScrolled(window.scrollY > 0);
9 | }
10 | onScroll();
11 | window.addEventListener('scroll', onScroll, { passive: true });
12 | return () => {
13 | window.removeEventListener('scroll', onScroll);
14 | };
15 | }, []);
16 |
17 | return {
18 | isScrolled,
19 | };
20 | }
--------------------------------------------------------------------------------
/apps/product-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "product-e2e",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "implicitDependencies": ["product"],
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/jest:jest",
9 | "outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"],
10 | "options": {
11 | "jestConfig": "apps/product-e2e/jest.config.ts",
12 | "passWithNoTests": true
13 | },
14 | "dependsOn": ["product:build"]
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/libs/backend/email/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"],
7 | "target": "es2021",
8 | "strictNullChecks": true,
9 | "noImplicitAny": true,
10 | "strictBindCallApply": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true
13 | },
14 | "include": ["src/**/*.ts"],
15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/libs/backend/payment/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"],
7 | "target": "es2021",
8 | "strictNullChecks": true,
9 | "noImplicitAny": true,
10 | "strictBindCallApply": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true
13 | },
14 | "include": ["src/**/*.ts"],
15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/libs/backend/workflows/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"],
7 | "target": "es2021",
8 | "strictNullChecks": true,
9 | "noImplicitAny": true,
10 | "strictBindCallApply": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true
13 | },
14 | "include": ["src/**/*.ts"],
15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/apps/web/app/pages/PageLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Layout, LayoutProps } from '@projectx/ui';
2 | import React from 'react';
3 |
4 | import { useAuth } from '~/providers';
5 |
6 | type PageLayoutProps = LayoutProps;
7 |
8 | export const PageLayout: React.FC = ({ children, ...props }) => {
9 | const {
10 | user,
11 | isAuthenticated,
12 | } = useAuth();
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | };
19 |
20 | export default PageLayout;
--------------------------------------------------------------------------------
/apps/web/app/routes/product.tsx:
--------------------------------------------------------------------------------
1 | import type { MetaFunction } from '@remix-run/node';
2 | import { ProductDetail } from '~/pages/ProductDetail';
3 | import PageLayout from '~/pages/PageLayout';
4 |
5 | export const meta: MetaFunction = () => {
6 | return [
7 | { title: 'ProjectX - Product Detail' },
8 | { name: 'description', content: 'View the details of a product.' },
9 | ];
10 | };
11 |
12 | export default function Index() {
13 | return (
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/app/providers/auth/hook.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { AuthContext } from './context';
4 |
5 | export const useAuth = () => {
6 | const context = useContext(AuthContext);
7 | if (!context) {
8 | throw new Error('useAuth must be used within an AuthProvider');
9 | }
10 | return context;
11 | };
12 |
13 | export const useAuthToken = () => {
14 | const { accessToken } = useAuth();
15 | return accessToken;
16 | };
17 |
18 | export const useAuthUser = () => {
19 | const { user } = useAuth();
20 | return user;
21 | };
22 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/utils.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 |
3 | import { StoreState } from './store';
4 | import { Workflow, WorkflowType } from './types';
5 |
6 | export const useWorkflowsByType = >(
7 | store: StoreState,
8 | workflowType: WorkflowType,
9 | email?: string,
10 | ) => {
11 | return useMemo(() => {
12 | return ((store?.workflows[workflowType] as T[]) || []).filter(
13 | (w) => !w.email || `${w.email}` === `${email}`,
14 | );
15 | }, [store?.workflows[workflowType], email]);
16 | };
17 |
--------------------------------------------------------------------------------
/libs/frontend/ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@projectx/ui",
3 | "version": "0.0.1",
4 | "main": "./index.js",
5 | "types": "./index.d.ts",
6 | "exports": {
7 | ".": {
8 | "import": "./index.mjs",
9 | "require": "./index.js"
10 | },
11 | "./tailwind-config": {
12 | "import": "./tailwind.config.mjs",
13 | "require": "./tailwind.config.js"
14 | }
15 | },
16 | "files": [
17 | "tailwind.config.ts",
18 | "styles"
19 | ],
20 | "peerDependencies": {
21 | "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/button/Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { Button } from './Button';
3 |
4 | const meta: Meta = {
5 | component: Button,
6 | title: 'Buttons/Button',
7 | };
8 | export default meta;
9 | type Story = StoryObj;
10 |
11 | export const Primary: Story = {
12 | render: (args) => (
13 |
14 |
15 |
16 | ),
17 | };
18 |
--------------------------------------------------------------------------------
/libs/models/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noPropertyAccessFromIndexSignature": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/libs/backend/db/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noPropertyAccessFromIndexSignature": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/libs/models/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"],
7 | "target": "es2021",
8 | "strictNullChecks": true,
9 | "noImplicitAny": true,
10 | "strictBindCallApply": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "composite": true,
14 | "noEmit": false
15 | },
16 | "include": ["src/**/*.ts"],
17 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/product/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2 | const { join } = require('path');
3 |
4 | module.exports = {
5 | output: {
6 | path: join(__dirname, '../../dist/apps/product'),
7 | },
8 | plugins: [
9 | new NxAppWebpackPlugin({
10 | target: 'node',
11 | compiler: 'tsc',
12 | main: './src/main.ts',
13 | tsConfig: './tsconfig.app.json',
14 | assets: ['./src/assets'],
15 | optimization: false,
16 | outputHashing: 'none',
17 | generatePackageJson: true,
18 | }),
19 | ],
20 | };
21 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/user/user.decorator.ts:
--------------------------------------------------------------------------------
1 |
2 | import { createParamDecorator, ExecutionContext, UnauthorizedException } from '@nestjs/common';
3 |
4 | import { AuthUser } from './user.interface';
5 |
6 | export const AuthenticatedUser = createParamDecorator(
7 | (data: keyof AuthUser, ctx: ExecutionContext) => {
8 | const request = ctx.switchToHttp().getRequest();
9 | const user = request.user as AuthUser;
10 | if (!user) {
11 | throw new UnauthorizedException();
12 | }
13 | return data ? user[data] : user;
14 | },
15 | );
16 |
--------------------------------------------------------------------------------
/libs/backend/payment/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noPropertyAccessFromIndexSignature": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/libs/backend/workflows/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noPropertyAccessFromIndexSignature": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/auth-e2e/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'auth-e2e',
3 | preset: '../../jest.preset.js',
4 | globalSetup: '/src/support/global-setup.ts',
5 | globalTeardown: '/src/support/global-teardown.ts',
6 | setupFiles: ['/src/support/test-setup.ts'],
7 | testEnvironment: 'node',
8 | transform: {
9 | '^.+\\.[tj]s$': [
10 | 'ts-jest',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | },
14 | ],
15 | },
16 | moduleFileExtensions: ['ts', 'js', 'html'],
17 | coverageDirectory: '../../coverage/auth-e2e',
18 | };
19 |
--------------------------------------------------------------------------------
/apps/auth/src/config/env.config.ts:
--------------------------------------------------------------------------------
1 | import { Environment } from '@projectx/models';
2 | import {
3 | IsDefined,
4 | IsEnum,
5 | IsNotEmpty,
6 | IsInt,
7 | IsString,
8 | Max,
9 | Min,
10 | } from 'class-validator';
11 |
12 | export class EnvironmentVariables {
13 | @IsEnum(Environment)
14 | @IsDefined()
15 | NODE_ENV: Environment;
16 |
17 | @IsInt()
18 | @Min(0)
19 | @Max(65535)
20 | @IsDefined()
21 | AUTH_PORT: number;
22 |
23 | @IsString()
24 | @IsNotEmpty()
25 | JWT_SECRET: string;
26 |
27 | @IsString()
28 | @IsNotEmpty()
29 | SENDGRID_API_KEY: string;
30 | }
31 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the official Node.js image as the base image
2 | FROM node:20.10.0
3 |
4 | # Set the working directory inside the container
5 | WORKDIR /app
6 |
7 | # Copy package.json and package-lock.json
8 | COPY package.json .
9 | COPY package-lock.json .
10 |
11 | # Clean npm cache and rebuild node-gyp
12 | RUN npm cache clean --force
13 | RUN npm rebuild node-gyp
14 |
15 | # Install bash
16 | RUN apt-get update && apt-get install -y bash
17 |
18 | # Install dependencies
19 | RUN npm ci
20 |
21 | # Copy the application files
22 | COPY . .
23 |
24 | # Install Nx globally
25 | RUN npm add --global nx@latest
--------------------------------------------------------------------------------
/apps/order-e2e/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'order-e2e',
3 | preset: '../../jest.preset.js',
4 | globalSetup: '/src/support/global-setup.ts',
5 | globalTeardown: '/src/support/global-teardown.ts',
6 | setupFiles: ['/src/support/test-setup.ts'],
7 | testEnvironment: 'node',
8 | transform: {
9 | '^.+\\.[tj]s$': [
10 | 'ts-jest',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | },
14 | ],
15 | },
16 | moduleFileExtensions: ['ts', 'js', 'html'],
17 | coverageDirectory: '../../coverage/order-e2e',
18 | };
19 |
--------------------------------------------------------------------------------
/apps/product/src/app/app.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test } from '@nestjs/testing';
2 |
3 | import { AppService } from './app.service';
4 |
5 | describe('AppService', () => {
6 | let service: AppService;
7 |
8 | beforeAll(async () => {
9 | const app = await Test.createTestingModule({
10 | providers: [AppService],
11 | }).compile();
12 |
13 | service = app.get(AppService);
14 | });
15 |
16 | describe('getData', () => {
17 | it('should return "Hello API"', () => {
18 | expect(service.getData()).toEqual({ message: 'Hello API' });
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/apps/product-e2e/jest.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | displayName: 'product-e2e',
3 | preset: '../../jest.preset.js',
4 | globalSetup: '/src/support/global-setup.ts',
5 | globalTeardown: '/src/support/global-teardown.ts',
6 | setupFiles: ['/src/support/test-setup.ts'],
7 | testEnvironment: 'node',
8 | transform: {
9 | '^.+\\.[tj]s$': [
10 | 'ts-jest',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | },
14 | ],
15 | },
16 | moduleFileExtensions: ['ts', 'js', 'html'],
17 | coverageDirectory: '../../coverage/product-e2e',
18 | };
19 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/theme/ThemeButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { ThemeButton } from './ThemeButton';
3 |
4 | const meta: Meta = {
5 | component: ThemeButton,
6 | title: 'Buttons/ThemeButton',
7 | };
8 | export default meta;
9 | type Story = StoryObj;
10 |
11 | export const Primary: Story = {
12 | render: () => (
13 |
14 |
15 |
16 | ),
17 | };
18 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/health/health.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule } from '@nestjs/config';
3 | import { TerminusModule } from '@nestjs/terminus';
4 | import { PrismaModule } from '@projectx/db';
5 |
6 | import { HealthController } from './health.controller';
7 |
8 | @Module({
9 | imports: [
10 | ConfigModule,
11 | PrismaModule,
12 | TerminusModule.forRoot({
13 | errorLogStyle: 'pretty',
14 | gracefulShutdownTimeoutMs: 1000,
15 | }),
16 | ],
17 | controllers: [HealthController],
18 | providers: [],
19 | })
20 | export class HealthModule {}
21 |
--------------------------------------------------------------------------------
/libs/backend/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noPropertyAccessFromIndexSignature": true
11 | },
12 | "files": [],
13 | "include": [
14 | "types/**/*.d.ts"
15 | ],
16 | "references": [
17 | {
18 | "path": "./tsconfig.lib.json"
19 | },
20 | {
21 | "path": "./tsconfig.spec.json"
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/libs/backend/email/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noPropertyAccessFromIndexSignature": true,
11 | "esModuleInterop": true
12 | },
13 | "files": [],
14 | "include": [],
15 | "references": [
16 | {
17 | "path": "./tsconfig.lib.json"
18 | },
19 | {
20 | "path": "./tsconfig.spec.json"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/libs/frontend/ui/postcss.config.js:
--------------------------------------------------------------------------------
1 | const { join } = require('path');
2 |
3 | // Note: If you use library-specific PostCSS/Tailwind configuration then you should remove the `postcssConfig` build
4 | // option from your application's configuration (i.e. project.json).
5 | //
6 | // See: https://nx.dev/guides/using-tailwind-css-in-react#step-4:-applying-configuration-to-libraries
7 |
8 | module.exports = {
9 | plugins: {
10 | 'postcss-import': {},
11 | 'tailwindcss/nesting': {},
12 | tailwindcss: {
13 | config: join(__dirname, 'tailwind.config.ts'),
14 | },
15 | autoprefixer: {},
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/libs/frontend/ui/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "types": [
6 | "node",
7 | "@nx/react/typings/cssmodule.d.ts",
8 | "@nx/react/typings/image.d.ts",
9 | "vite/client"
10 | ]
11 | },
12 | "exclude": [
13 | "**/*.spec.ts",
14 | "**/*.test.ts",
15 | "**/*.spec.tsx",
16 | "**/*.test.tsx",
17 | "**/*.spec.js",
18 | "**/*.test.js",
19 | "**/*.spec.jsx",
20 | "**/*.test.jsx"
21 | ],
22 | "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
23 | }
24 |
--------------------------------------------------------------------------------
/deployment/scripts/search-attributes.sh:
--------------------------------------------------------------------------------
1 | until tctl workflow list > /dev/null 2>&1; do
2 | echo 'Waiting for Temporal server to be ready...'
3 | sleep 5
4 | done
5 |
6 | echo 'Y' | tctl admin cluster add-search-attributes --name OrderId --type INT && echo 'Search attribute OrderID added successfully.'
7 | echo 'Y' | tctl admin cluster add-search-attributes --name UserId --type INT && echo 'Search attribute UserID added successfully.'
8 | echo 'Y' | tctl admin cluster add-search-attributes --name Email --type TEXT && echo 'Search attribute Email added successfully.'
9 |
10 | echo 'Temporal Search attributes added successfully.'
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Mic.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { MicIcon } from './Mic';
3 |
4 | const meta: Meta = {
5 | component: MicIcon,
6 | title: 'Icons/Mic',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Logger } from '@nestjs/common';
2 | import { JwtService } from '@nestjs/jwt';
3 | import { UserDto } from '@projectx/models';
4 |
5 | @Injectable()
6 | export class AuthService {
7 | readonly logger = new Logger(AuthService.name);
8 | constructor(private readonly jwtService: JwtService) {}
9 |
10 | async createAccessToken(user: UserDto) {
11 | this.logger.log(`createAccessToken(${user.email}) - creating token`);
12 | return await this.jwtService.signAsync({
13 | sub: user.id,
14 | email: user.email,
15 | username: user.username,
16 | });
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Love.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { LoveIcon } from './Love';
3 |
4 | const meta: Meta = {
5 | component: LoveIcon,
6 | title: 'Icons/Love',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Menu.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { MenuIcon } from './Menu';
3 |
4 | const meta: Meta = {
5 | component: MenuIcon,
6 | title: 'Icons/Menu',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/apps/auth/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2 | const { join } = require('path');
3 |
4 | module.exports = {
5 | output: {
6 | path: join(__dirname, '../../dist/apps/auth'),
7 | },
8 | devtool: 'source-map',
9 | plugins: [
10 | new NxAppWebpackPlugin({
11 | target: 'node',
12 | compiler: 'tsc',
13 | main: './src/main.ts',
14 | tsConfig: './tsconfig.app.json',
15 | assets: ['./src/assets', './src/workflows'],
16 | optimization: false,
17 | outputHashing: 'none',
18 | generatePackageJson: true,
19 | sourceMap: true
20 | }),
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/apps/order/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2 | const { join } = require('path');
3 |
4 | module.exports = {
5 | output: {
6 | path: join(__dirname, '../../dist/apps/order'),
7 | },
8 | devtool: 'source-map',
9 | plugins: [
10 | new NxAppWebpackPlugin({
11 | target: 'node',
12 | compiler: 'tsc',
13 | main: './src/main.ts',
14 | tsConfig: './tsconfig.app.json',
15 | assets: ['./src/assets', './src/workflows'],
16 | optimization: false,
17 | outputHashing: 'none',
18 | generatePackageJson: true,
19 | sourceMap: true
20 | }),
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Close.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { CloseIcon } from './Close';
3 |
4 | const meta: Meta = {
5 | component: CloseIcon,
6 | title: 'Icons/Close',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
23 |
--------------------------------------------------------------------------------
/apps/web/app/config/env.server.ts:
--------------------------------------------------------------------------------
1 | import {
2 | authAPIUrl,
3 | environment,
4 | orderAPIUrl,
5 | productAPIUrl,
6 | stripePublishableKey,
7 | } from './app.config.server';
8 |
9 | export function getEnv() {
10 | return {
11 | NODE_ENV: environment,
12 | AUTH_API_URL: authAPIUrl,
13 | ORDER_API_URL: orderAPIUrl,
14 | PRODUCT_API_URL: productAPIUrl,
15 | STRIPE_PUBLISHABLE_KEY: stripePublishableKey,
16 | };
17 | }
18 |
19 | export type ENV = ReturnType;
20 |
21 | declare global {
22 | // eslint-disable-next-line no-var
23 | var ENV: ReturnType;
24 | interface window {
25 | ENV: ENV;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/web/remix.config.js:
--------------------------------------------------------------------------------
1 | import { createWatchPaths } from '@nx/remix';
2 | import { dirname } from 'path';
3 | import { fileURLToPath } from 'url';
4 |
5 | const __dirname = dirname(fileURLToPath(import.meta.url));
6 |
7 | /**
8 | * @type {import('@remix-run/dev').AppConfig}
9 | */
10 | export default {
11 | tailwind: true,
12 | postcss: true,
13 | ignoredRouteFiles: ['**/.*'],
14 | serverDependenciesToBundle: [
15 | /^remix-utils.*/
16 | ],
17 | // appDirectory: "app",
18 | // assetsBuildDirectory: "public/build",
19 | // serverBuildPath: "build/index.js",
20 | // publicPath: "/build/",
21 | watchPaths: () => createWatchPaths(__dirname),
22 | };
23 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/workflows/index.ts:
--------------------------------------------------------------------------------
1 | import { defineSignal, defineUpdate } from '@temporalio/workflow';
2 |
3 | export * from './state';
4 |
5 | export enum WorkflowTypes {
6 | Order = 'Order',
7 | }
8 |
9 | export type WorkflowParentData = {
10 | workflowId: string;
11 | runId: string;
12 | };
13 |
14 | // DEFINE SIGNALS
15 | /**
16 | * Send a request to cancel the workflow
17 | */
18 | export const cancelWorkflowSignal = defineSignal('cancelWorkflowSignal');
19 |
20 | // DEFINE UPDATES
21 | /**
22 | * Try to cancel the workflow and return true if successful
23 | */
24 | export const cancelWorkflowUpdate = defineUpdate('cancelWorkflowUpdate');
25 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/GitHub.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { GitHubIcon } from './GitHub';
3 |
4 | const meta: Meta = {
5 | component: GitHubIcon,
6 | title: 'Icons/GitHub',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Search.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { SearchIcon } from './Search';
3 |
4 | const meta: Meta = {
5 | component: SearchIcon,
6 | title: 'Icons/Search',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Close.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const CloseIcon: React.FC = ({
9 | className,
10 | color = 'none'
11 | }) => {
12 | return (
13 |
26 | );
27 | };
28 |
29 | export default CloseIcon;
30 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Facebook.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const FacebookIcon: React.FC = ({
9 | className,
10 | color = 'currentColor'
11 | }) => {
12 | return (
13 |
24 | );
25 | };
26 |
27 | export default FacebookIcon;
28 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Menu.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const MenuIcon: React.FC = ({
9 | className,
10 | color = 'none'
11 | }) => {
12 | return (
13 |
26 | );
27 | };
28 |
29 | export default MenuIcon;
30 |
--------------------------------------------------------------------------------
/apps/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "web",
4 | "description": "",
5 | "license": "",
6 | "scripts": {},
7 | "type": "module",
8 | "dependencies": {
9 | "@remix-run/node": "2.13.1",
10 | "@remix-run/react": "2.13.1",
11 | "@remix-run/serve": "2.13.1",
12 | "isbot": "4.4.0",
13 | "react": "18.3.1",
14 | "react-dom": "18.3.1"
15 | },
16 | "devDependencies": {
17 | "@remix-run/dev": "^2.13.1",
18 | "@types/react": "^18.3.1",
19 | "@types/react-dom": "^18.3.1",
20 | "eslint": "^8.56.0",
21 | "typescript": "~5.5.2"
22 | },
23 | "engines": {
24 | "node": ">=14"
25 | },
26 | "sideEffects": false
27 | }
28 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Facebook.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { FacebookIcon } from './Facebook';
3 |
4 | const meta: Meta = {
5 | component: FacebookIcon,
6 | title: 'Icons/Facebook',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Instagram.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { InstagramIcon } from './Instagram';
3 |
4 | const meta: Meta = {
5 | component: InstagramIcon,
6 | title: 'Icons/Instagram',
7 | args: {
8 | className: 'w-20 h-20 bg-blue-500',
9 | color: 'white',
10 | },
11 | };
12 | export default meta;
13 | type Story = StoryObj;
14 |
15 | export const Primary: Story = {
16 | render: (args) => (
17 |
18 |
19 |
20 | ),
21 | };
22 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/providers/router.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentType } from 'react';
2 | import { createMemoryRouter, RouterProvider, RouteObject } from 'react-router-dom';
3 |
4 | export function withRouterProvider(
5 | WrappedComponent: ComponentType,
6 | ): ComponentType {
7 | return function (props: T) {
8 | const routes: RouteObject[] = [
9 | {
10 | path: '/',
11 | element: ,
12 | },
13 | ];
14 |
15 | const router = createMemoryRouter(routes, {
16 | initialEntries: ['/'], // You can set the initial route here
17 | });
18 |
19 | return ;
20 | };
21 | }
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/inputs/search/Search.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { Search } from './Search';
3 |
4 | const meta: Meta = {
5 | component: Search,
6 | title: 'Search',
7 | args: {
8 | className: '',
9 | autoFocus: true,
10 | placeholder: 'Search products, brands, etc',
11 | },
12 | };
13 | export default meta;
14 | type Story = StoryObj;
15 |
16 | export const Primary: Story = {
17 | render: (args) => (
18 |
19 |
20 |
21 | ),
22 | };
23 |
24 |
--------------------------------------------------------------------------------
/apps/web/app/config/utils.server.ts:
--------------------------------------------------------------------------------
1 | import { Environment } from '@projectx/models';
2 |
3 | function getRequiredEnvVarFromObj(
4 | obj: Record,
5 | key: string,
6 | devValue: unknown = `${key}-dev-value`
7 | ) {
8 | let value = devValue;
9 | const envVal = obj[key];
10 | if (envVal) {
11 | value = envVal;
12 | } else if (obj.ENVIRONMENT !== Environment.Development) {
13 | throw new Error(`${key} is a required env variable`);
14 | }
15 | return value as T;
16 | }
17 |
18 | export function getRequiredServerEnvVar(
19 | key: string,
20 | devValue?: unknown
21 | ) {
22 | return getRequiredEnvVarFromObj(process.env, key, devValue);
23 | }
24 |
--------------------------------------------------------------------------------
/apps/auth/src/app/user/user.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Logger } from '@nestjs/common';
2 | import { AuthUser } from '@projectx/core';
3 | import { UserRepositoryService } from '@projectx/db';
4 |
5 | @Injectable()
6 | export class UserService {
7 | readonly logger = new Logger(UserService.name);
8 | constructor(
9 | private readonly userService: UserRepositoryService
10 | ) {}
11 |
12 | findOne(user: AuthUser) {
13 | this.logger.log(`findOne(${user.id})`, user);
14 | return this.userService.findOneByEmail(user.email);
15 | }
16 |
17 | getOrCreate(
18 | ...params: Parameters
19 | ) {
20 | return this.userService.getOrCreate(...params);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/apps/product/src/app/app.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Logger } from '@nestjs/common';
2 | import { ProductRepositoryService } from '@projectx/db';
3 | import { ProductDto } from '@projectx/models';
4 |
5 | @Injectable()
6 | export class AppService {
7 | readonly logger = new Logger(AppService.name);
8 |
9 | constructor(private readonly productRepository: ProductRepositoryService) {}
10 |
11 | /**
12 | * Retrieves all available products.
13 | * @returns Array of ProductDto containing the product information.
14 | */
15 | async getProducts(): Promise {
16 | this.logger.log('getProducts() - retrieving all products');
17 | return await this.productRepository.findProducts();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": [
6 | "vitest/globals",
7 | "vitest/importMeta",
8 | "vite/client",
9 | "node",
10 | "vitest"
11 | ]
12 | },
13 | "include": [
14 | "vite.config.ts",
15 | "vitest.config.ts",
16 | "app/**/*.ts",
17 | "app/**/*.tsx",
18 | "app/**/*.js",
19 | "app/**/*.jsx",
20 | "tests/**/*.spec.ts",
21 | "tests/**/*.test.ts",
22 | "tests/**/*.spec.tsx",
23 | "tests/**/*.test.tsx",
24 | "tests/**/*.spec.js",
25 | "tests/**/*.test.js",
26 | "tests/**/*.spec.jsx",
27 | "tests/**/*.test.jsx"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web/app/config/app.config.server.ts:
--------------------------------------------------------------------------------
1 | import type { Environment } from '@projectx/models';
2 |
3 | import { getRequiredServerEnvVar } from './utils.server';
4 |
5 | export const environment = getRequiredServerEnvVar('NODE_ENV');
6 | export const sessionSecret = getRequiredServerEnvVar(
7 | 'SESSION_SECRET',
8 | 'MY_SECRET_KEY'
9 | );
10 | export const stripePublishableKey = getRequiredServerEnvVar('STRIPE_PUBLISHABLE_KEY');
11 | export const authAPIUrl = getRequiredServerEnvVar('AUTH_API_URL', 'http://localhost:8081');
12 | export const orderAPIUrl = getRequiredServerEnvVar('ORDER_API_URL', 'http://localhost:8082');
13 | export const productAPIUrl = getRequiredServerEnvVar('PRODUCT_API_URL', 'http://localhost:8083');
--------------------------------------------------------------------------------
/apps/product/src/app/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 |
3 | import { AppController } from './app.controller';
4 | import { AppService } from './app.service';
5 |
6 | describe('AppController', () => {
7 | let app: TestingModule;
8 |
9 | beforeAll(async () => {
10 | app = await Test.createTestingModule({
11 | controllers: [AppController],
12 | providers: [AppService],
13 | }).compile();
14 | });
15 |
16 | describe('getData', () => {
17 | it('should return "Hello API"', () => {
18 | const appController = app.get(AppController);
19 | expect(appController.getData()).toEqual({ message: 'Hello API' });
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Love.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const LoveIcon: React.FC = ({
9 | className,
10 | color = 'currentColor'
11 | }) => {
12 | return (
13 |
24 | );
25 | };
26 |
27 | export default LoveIcon;
28 |
--------------------------------------------------------------------------------
/apps/order/src/app/activities/activities.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { OrderWorkflowData } from '@projectx/core';
3 |
4 | import { OrderService } from '../order/order.service';
5 |
6 | @Injectable()
7 | export class ActivitiesService {
8 | constructor(
9 | public readonly orderService: OrderService
10 | ) {}
11 |
12 | async createOrder(data: OrderWorkflowData) {
13 | return await this.orderService.createOrder(data);
14 | }
15 |
16 | async reportPaymentFailed(orderId: number) {
17 | return this.orderService.reportPaymentFailed(orderId);
18 | }
19 |
20 | async reportPaymentConfirmed(orderId: number) {
21 | return this.orderService.reportPaymentConfirmed(orderId);
22 | }
23 | }
--------------------------------------------------------------------------------
/apps/web/app/routes/email.$template.tsx:
--------------------------------------------------------------------------------
1 | import { EmailTemplates, getEmailTemplate } from '@projectx/email';
2 | import { json, LoaderFunctionArgs } from '@remix-run/node';
3 | import invariant from 'tiny-invariant';
4 |
5 | export const loader = ({ params }: LoaderFunctionArgs) => {
6 | invariant(params?.template, 'template is required');
7 | const html = getEmailTemplate(params.template as unknown as EmailTemplates, {
8 | token: '123',
9 | orderId: '123',
10 | userName: 'John Doe',
11 | });
12 | if (!html) {
13 | return json(null, { status: 404 });
14 | }
15 | return new Response(html, {
16 | status: 200,
17 | headers: {
18 | 'Content-Type': 'text/html',
19 | encoding: 'UTF-8',
20 | },
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/apps/order/src/config/env.config.ts:
--------------------------------------------------------------------------------
1 | import { Environment } from '@projectx/models';
2 | import {
3 | IsDefined,
4 | IsEnum,
5 | IsNotEmpty,
6 | IsInt,
7 | IsString,
8 | Max,
9 | Min,
10 | } from 'class-validator';
11 |
12 | export class EnvironmentVariables {
13 | @IsEnum(Environment)
14 | @IsDefined()
15 | NODE_ENV: Environment;
16 |
17 | @IsInt()
18 | @Min(0)
19 | @Max(65535)
20 | @IsDefined()
21 | ORDER_PORT: number;
22 |
23 | @IsString()
24 | @IsNotEmpty()
25 | JWT_SECRET: string;
26 |
27 | @IsString()
28 | @IsNotEmpty()
29 | STRIPE_SECRET_KEY: string;
30 |
31 | @IsString()
32 | @IsNotEmpty()
33 | STRIPE_WEBHOOK_SECRET: string;
34 |
35 | @IsString()
36 | @IsNotEmpty()
37 | SENDGRID_API_KEY: string;
38 | }
39 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/button/Button.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps } from 'react';
2 |
3 | export type ButtonProps = ComponentProps<'button'> & {
4 | variant?: 'primary' | 'secondary' | 'accent' | 'ghost';
5 | };
6 |
7 | export const Button: React.FC = ({
8 | children,
9 | variant = 'primary',
10 | className = '',
11 | ...props
12 | }) => {
13 | const baseStyles = 'btn';
14 | const variantStyles = {
15 | primary: 'btn-primary',
16 | secondary: 'btn-secondary',
17 | accent: 'btn-accent',
18 | ghost: 'btn-ghost',
19 | };
20 | return (
21 |
24 | );
25 | };
26 |
27 | export default Button;
28 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Instagram.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const InstagramIcon: React.FC = ({
9 | className,
10 | color = 'currentColor'
11 | }) => {
12 | return (
13 |
24 | );
25 | };
26 |
27 | export default InstagramIcon;
28 |
--------------------------------------------------------------------------------
/apps/web/app/routes/order-summary.tsx:
--------------------------------------------------------------------------------
1 | import type { LoaderFunction, MetaFunction } from '@remix-run/node';
2 |
3 | import OrderSummary from '~/pages/OrderSummary';
4 | import { getAccessTokenOrRedirect } from '~/cookies/auth.server';
5 | import PageLayout from '~/pages/PageLayout';
6 |
7 | export const meta: MetaFunction = () => {
8 | return [
9 | { title: 'ProjectX - Order Summary' },
10 | { name: 'description', content: 'View your order summary.' },
11 | ];
12 | };
13 |
14 | export const loader: LoaderFunction = async ({ request }) => {
15 | await getAccessTokenOrRedirect(request);
16 | return {};
17 | };
18 |
19 | export default function Index() {
20 | return (
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/apps/web/app/routes/order-history.tsx:
--------------------------------------------------------------------------------
1 | import type { LoaderFunction, MetaFunction } from '@remix-run/node';
2 |
3 | import { OrderHistory } from '~/pages/OrderHistory';
4 | import { getAccessTokenOrRedirect } from '~/cookies/auth.server';
5 | import PageLayout from '~/pages/PageLayout';
6 |
7 | export const meta: MetaFunction = () => {
8 | return [
9 | { title: 'ProjectX - Order History' },
10 | { name: 'description', content: 'View your order history.' },
11 | ];
12 | };
13 |
14 | export const loader: LoaderFunction = async ({ request }) => {
15 | await getAccessTokenOrRedirect(request);
16 | return {};
17 | };
18 |
19 | export default function Index() {
20 | return (
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Search.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const SearchIcon: React.FC = ({
9 | className,
10 | color = 'currentColor'
11 | }) => {
12 | return (
13 |
28 | );
29 | };
30 |
31 | export default SearchIcon;
32 |
--------------------------------------------------------------------------------
/apps/order/src/app/app.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { OrderRepositoryService } from '@projectx/db';
2 | import { createMock } from '@golevelup/ts-jest';
3 | import { Test } from '@nestjs/testing';
4 |
5 | import { AppService } from './app.service';
6 |
7 | describe('AppService', () => {
8 | let service: AppService;
9 |
10 | beforeAll(async () => {
11 | const app = await Test.createTestingModule({
12 | providers: [
13 | AppService,
14 | { provide: OrderRepositoryService, useValue: createMock() },
15 | ],
16 | }).compile();
17 |
18 | service = app.get(AppService);
19 | });
20 |
21 | describe('findAll', () => {
22 | it('should return something', () => {
23 | expect(service.findAll()).toBeDefined();
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/types/order.ts:
--------------------------------------------------------------------------------
1 | import type { CreateOrderDto, OrderStatusResponseDto } from "@projectx/models";
2 | import { Workflow } from "./workflow";
3 |
4 | export enum OrderStatus {
5 | Pending = 'Pending',
6 | Confirmed = 'Confirmed',
7 | Shipped = 'Shipped',
8 | Delivered = 'Delivered',
9 | Cancelled = 'Cancelled',
10 | Failed = 'Failed',
11 | }
12 |
13 | export const OrderSuccessStatus = [
14 | OrderStatus.Confirmed,
15 | OrderStatus.Shipped,
16 | OrderStatus.Delivered,
17 | ];
18 |
19 | export const OrderFailureStatus = [
20 | OrderStatus.Failed,
21 | OrderStatus.Cancelled,
22 | ];
23 |
24 |
25 | export type OrderWorkflowData = CreateOrderDto & {
26 | response?: OrderStatusResponseDto;
27 | };
28 |
29 | export type OrderWorkflow = Workflow;
30 |
31 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "remix.env.d.ts",
5 | "app/**/*.ts",
6 | "app/**/*.tsx",
7 | "app/**/*.js",
8 | "app/**/*.jsx"
9 | ],
10 | "exclude": [
11 | "tests/**/*.spec.ts",
12 | "tests/**/*.test.ts",
13 | "tests/**/*.spec.tsx",
14 | "tests/**/*.test.tsx",
15 | "tests/**/*.spec.js",
16 | "tests/**/*.test.js",
17 | "tests/**/*.spec.jsx",
18 | "tests/**/*.test.jsx",
19 | "vite.config.ts",
20 | "vite.config.mts",
21 | "vitest.config.ts",
22 | "vitest.config.mts",
23 | "src/**/*.test.ts",
24 | "src/**/*.spec.ts",
25 | "src/**/*.test.tsx",
26 | "src/**/*.spec.tsx",
27 | "src/**/*.test.js",
28 | "src/**/*.spec.js",
29 | "src/**/*.test.jsx",
30 | "src/**/*.spec.jsx"
31 | ]
32 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | dist
5 | tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
41 | .nx/cache
42 | .nx/workspace-data
43 |
44 |
45 | storybook-static
46 |
47 | # env files
48 | .env
49 | **/vite.config.{js,ts,mjs,mts,cjs,cts}.timestamp*
--------------------------------------------------------------------------------
/apps/order/src/app/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { createMock } from '@golevelup/ts-jest';
2 | import { Test, TestingModule } from '@nestjs/testing';
3 |
4 | import { AppController } from './app.controller';
5 | import { AppService } from './app.service';
6 |
7 | describe('AppController', () => {
8 | let app: TestingModule;
9 |
10 | beforeAll(async () => {
11 | app = await Test.createTestingModule({
12 | controllers: [AppController],
13 | providers: [
14 | { provide: AppService, useValue: createMock() },
15 | ],
16 | }).compile();
17 | });
18 |
19 | describe('findAll', () => {
20 | it('should return something', () => {
21 | const appController = app.get(AppController);
22 | expect(appController.findAll()).toBeDefined();
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/common/mjml.ts:
--------------------------------------------------------------------------------
1 | import mjml from 'mjml';
2 |
3 | import { COLORS } from './constants';
4 | import { getHeadTemplate } from './head';
5 | import { getHeaderTemplate } from './header';
6 | import { getFooterTemplate } from './footer';
7 |
8 | export const getBodyTemplate = (
9 | body: string,
10 | title?: string,
11 | preview?: string
12 | ) => `
13 |
14 | ${getHeadTemplate(title, preview)}
15 |
16 |
17 | ${getHeaderTemplate()}
18 | ${body}
19 | ${getFooterTemplate()}
20 |
21 |
22 | `;
23 |
24 | export const getEmailHtml = (body: string) => {
25 | const { errors, html } = mjml(getBodyTemplate(body));
26 | if (errors?.length) {
27 | throw new Error(errors[0].formattedMessage);
28 | }
29 | return html;
30 | };
31 |
--------------------------------------------------------------------------------
/apps/web/app/routes/order-detail.tsx:
--------------------------------------------------------------------------------
1 | import type { LoaderFunction, MetaFunction } from '@remix-run/node';
2 |
3 | import { getAccessTokenOrRedirect } from '~/cookies/auth.server';
4 | import { OrderPage } from '~/pages/Order';
5 | import PageLayout from '~/pages/PageLayout';
6 |
7 | export const meta: MetaFunction = () => {
8 | return [
9 | { title: 'ProjectX - Order Detail' },
10 | {
11 | name: 'description',
12 | content:
13 | 'View the details of your order and manage your order settings.',
14 | },
15 | ];
16 | };
17 |
18 | export const loader: LoaderFunction = async ({ request }) => {
19 | await getAccessTokenOrRedirect(request);
20 | return {};
21 | };
22 |
23 | export default function Index() {
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web/app/routes/profile.tsx:
--------------------------------------------------------------------------------
1 | import type { LoaderFunction, MetaFunction } from '@remix-run/node';
2 |
3 | import { getAccessTokenOrRedirect } from '~/cookies/auth.server';
4 | import PageLayout from '~/pages/PageLayout';
5 | import { ProfilePage } from '~/pages/Profile';
6 |
7 | export const meta: MetaFunction = () => {
8 | return [
9 | { title: 'ProjectX - Profile' },
10 | {
11 | name: 'description',
12 | content:
13 | 'Display your profile information and manage your account settings.',
14 | },
15 | ];
16 | };
17 |
18 | export const loader: LoaderFunction = async ({ request }) => {
19 | await getAccessTokenOrRedirect(request);
20 | return null;
21 | };
22 |
23 | export default function Index() {
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/Mic.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const MicIcon: React.FC = ({
9 | className,
10 | color = 'currentColor'
11 | }) => {
12 | return (
13 |
28 | );
29 | };
30 |
31 | export default MicIcon;
32 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/useCurrentWorkflow.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { Workflow, WorkflowType } from './types';
3 | import { useStoreState } from './store';
4 |
5 | /**
6 | * Find a workflow by its type and predicate
7 | * It's used to determine if there is a existing workflow for a specific task
8 | */
9 | export const useCurrentWorkflow = >(
10 | workflowType: WorkflowType,
11 | predicate: (value: T, index: number, obj: T[]) => unknown,
12 | ) => {
13 | const store = useStoreState();
14 | const workflows = store?.workflows[workflowType] as T[];
15 | const currentWorkflow = useMemo(() => {
16 | const workflow = (workflows || []).find(predicate);
17 | return workflow;
18 | // eslint-disable-next-line
19 | }, [workflowType, workflows]);
20 |
21 | return currentWorkflow;
22 | };
23 |
--------------------------------------------------------------------------------
/libs/models/src/auth/login.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { Expose, Transform } from 'class-transformer';
3 | import {
4 | IsEmail,
5 | IsNotEmpty,
6 | IsInt,
7 | IsString,
8 | Max,
9 | MaxLength,
10 | Min,
11 | } from 'class-validator';
12 |
13 | import { transformToLowerCase } from '../transforms';
14 |
15 | export class AuthLoginDto {
16 | @ApiProperty({ description: 'Email address for the user' })
17 | @IsEmail()
18 | @IsNotEmpty()
19 | @Expose()
20 | @MaxLength(100)
21 | @Transform(({ value }) => transformToLowerCase(value))
22 | @IsString()
23 | email!: string;
24 | }
25 |
26 | export class AuthVerifyDto extends AuthLoginDto {
27 | @ApiProperty({ description: 'Code sent to the user' })
28 | @IsNotEmpty()
29 | @Expose()
30 | @Min(0)
31 | @Max(999999)
32 | @IsInt()
33 | code!: number;
34 | }
35 |
--------------------------------------------------------------------------------
/apps/web/app/routes/admin.tsx:
--------------------------------------------------------------------------------
1 | import type { LoaderFunction, MetaFunction } from '@remix-run/node';
2 |
3 | import { getAccessTokenOrRedirect } from '~/cookies/auth.server';
4 | import { AdminPage } from '~/pages/Admin';
5 | import PageLayout from '~/pages/PageLayout';
6 |
7 | export const meta: MetaFunction = () => {
8 | return [
9 | { title: 'ProjectX - Admin Dashboard' },
10 | {
11 | name: 'description',
12 | content:
13 | 'Display the admin dashboard to manage your orders, notifications, and other settings.',
14 | },
15 | ];
16 | };
17 |
18 | export const loader: LoaderFunction = async ({ request }) => {
19 | await getAccessTokenOrRedirect(request);
20 | return null;
21 | };
22 |
23 | export default function Index() {
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/navigation/Navigation.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { Navigation } from './Navigation';
3 | import { withRouterProvider } from '../providers/router';
4 |
5 | const meta: Meta = {
6 | component: Navigation,
7 | title: 'Navigation/Desktop',
8 | args: {
9 | sections: [
10 | {
11 | title: 'Home',
12 | links: [{ title: 'Home', href: '/' }],
13 | },
14 | ],
15 | },
16 | };
17 | export default meta;
18 | type Story = StoryObj;
19 |
20 | const NavigationWithProviders = withRouterProvider(Navigation);
21 |
22 | export const Primary: Story = {
23 | render: (args) => (
24 |
25 |
26 |
27 | ),
28 | };
--------------------------------------------------------------------------------
/libs/frontend/ui/tsconfig.storybook.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "emitDecoratorMetadata": true,
5 | "outDir": ""
6 | },
7 | "files": [
8 | "../../node_modules/@nx/react/typings/styled-jsx.d.ts",
9 | "../../node_modules/@nx/react/typings/cssmodule.d.ts",
10 | "../../node_modules/@nx/react/typings/image.d.ts"
11 | ],
12 | "exclude": [
13 | "src/**/*.spec.ts",
14 | "src/**/*.test.ts",
15 | "src/**/*.spec.js",
16 | "src/**/*.test.js",
17 | "src/**/*.spec.tsx",
18 | "src/**/*.test.tsx",
19 | "src/**/*.spec.jsx",
20 | "src/**/*.test.js"
21 | ],
22 | "include": [
23 | "src/**/*.stories.ts",
24 | "src/**/*.stories.js",
25 | "src/**/*.stories.jsx",
26 | "src/**/*.stories.tsx",
27 | "src/**/*.stories.mdx",
28 | ".storybook/*.js",
29 | ".storybook/*.ts"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/common/header.ts:
--------------------------------------------------------------------------------
1 | import {
2 | COLORS,
3 | FONT_SIZES,
4 | FONT_WEIGHTS,
5 | IMAGES,
6 | PROJECT_NAME,
7 | SPACING,
8 | } from './constants';
9 |
10 | export const getHeaderTemplate = () => `
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Welcome to ${PROJECT_NAME}
21 |
22 |
23 |
24 | `;
25 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | const nx = require('@nx/eslint-plugin');
2 |
3 | module.exports = [
4 | ...nx.configs['flat/base'],
5 | ...nx.configs['flat/typescript'],
6 | ...nx.configs['flat/javascript'],
7 | {
8 | ignores: ['**/dist'],
9 | },
10 | {
11 | files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
12 | rules: {
13 | '@nx/enforce-module-boundaries': [
14 | 'error',
15 | {
16 | enforceBuildableLibDependency: true,
17 | allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?js$'],
18 | depConstraints: [
19 | {
20 | sourceTag: '*',
21 | onlyDependOnLibsWithTags: ['*'],
22 | },
23 | ],
24 | },
25 | ],
26 | },
27 | },
28 | {
29 | files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
30 | // Override or add rules here
31 | rules: {},
32 | },
33 | ];
34 |
--------------------------------------------------------------------------------
/apps/auth/src/app/user/user.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { createMock } from '@golevelup/ts-jest';
2 | import { Test, TestingModule } from '@nestjs/testing';
3 |
4 | import { UserController } from './user.controller';
5 | import { UserService } from './user.service';
6 |
7 | describe('UserController', () => {
8 | let controller: UserController;
9 | let service: UserService;
10 |
11 | beforeEach(async () => {
12 | const module: TestingModule = await Test.createTestingModule({
13 | controllers: [UserController],
14 | providers: [
15 | { provide: UserService, useValue: createMock() },
16 | ],
17 | }).compile();
18 |
19 | controller = module.get(UserController);
20 | service = module.get(UserService);
21 | });
22 |
23 | it('should be defined', () => {
24 | expect(controller).toBeDefined();
25 | expect(service).toBeDefined();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/useWorkflows.ts:
--------------------------------------------------------------------------------
1 | import { useOrderWorkflow } from './internal/useOrderWorkflow';
2 | import { useStoreState } from './store';
3 | import { OrderWorkflow, WorkflowTypes } from './types';
4 | import { useWorkflowsByType } from './utils';
5 |
6 | export type WorkflowProps = {
7 | accessToken: string;
8 | email?: string;
9 | };
10 |
11 | /**
12 | * Connect the backend workflows to the frontend
13 | */
14 | export const useWorkflows = ({ accessToken, email }: WorkflowProps) => {
15 | // The store has all the existing workflows and the actions to update them
16 | const store = useStoreState();
17 |
18 | // Connect the workflow to manage the order transactions
19 | useOrderWorkflow({
20 | accessToken,
21 | workflows: useWorkflowsByType(
22 | store,
23 | WorkflowTypes.ORDER,
24 | email,
25 | ),
26 | });
27 |
28 | // Connect other workflows here...
29 | };
30 |
--------------------------------------------------------------------------------
/apps/product/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule } from '@nestjs/config';
3 | import { CoreModule, validateConfiguration } from '@projectx/core';
4 | import { DbModule } from '@projectx/db';
5 |
6 | import { EnvironmentVariables } from '../config/env.config';
7 | import appConfig from '../config/app.config';
8 | import swaggerConfig from '../config/swagger.config';
9 | import { AppController } from './app.controller';
10 | import { AppService } from './app.service';
11 |
12 | @Module({
13 | imports: [
14 | DbModule,
15 | CoreModule,
16 | ConfigModule.forRoot({
17 | isGlobal: true,
18 | load: [
19 | appConfig,
20 | swaggerConfig,
21 | ],
22 | validate: (config) => validateConfiguration(config, EnvironmentVariables),
23 | }),
24 | ],
25 | controllers: [AppController],
26 | providers: [AppService],
27 | })
28 | export class AppModule {}
29 |
--------------------------------------------------------------------------------
/libs/frontend/ui/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
2 | import type { StorybookConfig } from '@storybook/react-vite';
3 | import react from '@vitejs/plugin-react';
4 | import { mergeConfig } from 'vite';
5 |
6 | const config: StorybookConfig = {
7 | stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
8 | addons: ['@storybook/addon-essentials'],
9 | framework: {
10 | name: '@storybook/react-vite',
11 | options: {},
12 | },
13 | staticDirs: ['../../../../apps/web/public'],
14 | viteFinal: async (config) =>
15 | mergeConfig(config, {
16 | plugins: [react(), nxViteTsPaths()],
17 | }),
18 | };
19 |
20 | export default config;
21 |
22 | // To customize your Vite configuration you can use the viteFinal field.
23 | // Check https://storybook.js.org/docs/react/builders/vite#configuration
24 | // and https://nx.dev/recipes/storybook/custom-builder-configs
25 |
--------------------------------------------------------------------------------
/apps/web/app/pages/HomePage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Button } from '@projectx/ui';
4 |
5 | export const HomePage: React.FC = () => {
6 | return (
7 |
13 |
14 |
15 |
Welcome to Our Website
16 |
This is a sample home page using our new UI structure with DaisyUI and Framer Motion.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default HomePage;
--------------------------------------------------------------------------------
/apps/web/vitest.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { defineConfig } from 'vite';
3 | import react from '@vitejs/plugin-react';
4 | import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
5 | import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
6 |
7 | export default defineConfig({
8 | root: __dirname,
9 | cacheDir: '../../node_modules/.vite/apps/web',
10 | plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
11 | // Uncomment this if you are using workers.
12 | // worker: {
13 | // plugins: [ nxViteTsPaths() ],
14 | // },
15 | test: {
16 | setupFiles: ['test-setup.ts'],
17 | watch: false,
18 | globals: true,
19 | environment: 'jsdom',
20 | include: ['./tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
21 | reporters: ['default'],
22 | coverage: {
23 | reportsDirectory: '../../coverage/apps/test',
24 | provider: 'v8',
25 | },
26 | },
27 | });
28 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/checkout/CheckoutButton.tsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from '@remix-run/react';
2 | import { useCart } from 'react-use-cart';
3 | import { toast } from 'react-toastify';
4 | import { v4 as uuidv4 } from 'uuid';
5 |
6 | import Button from '../button/Button';
7 |
8 | export function CheckoutButton() {
9 | const { isEmpty } = useCart();
10 | const navigate = useNavigate();
11 | const handleCheckout = () => {
12 | alert(isEmpty);
13 | if (isEmpty) {
14 | toast.error(
15 | 'Your cart is empty, please add items to your cart before proceeding to checkout.'
16 | );
17 | return;
18 | }
19 | // Use UUID to generate a new referenceId
20 | const referenceId = uuidv4();
21 | navigate(`/checkout/${referenceId}`);
22 | };
23 |
24 | return (
25 |
28 | );
29 | }
30 |
31 | export default CheckoutButton;
32 |
--------------------------------------------------------------------------------
/apps/auth/src/app/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { createMock } from '@golevelup/ts-jest';
2 | import { Test, TestingModule } from '@nestjs/testing';
3 |
4 | import { AppController } from './app.controller';
5 | import { AppService } from './app.service';
6 |
7 | describe('AppController', () => {
8 | let app: TestingModule;
9 | let appService: AppService;
10 |
11 | beforeAll(async () => {
12 | app = await Test.createTestingModule({
13 | controllers: [AppController],
14 | providers: [
15 | { provide: AppService, useValue: createMock() },
16 | ],
17 | }).compile();
18 |
19 | appService = app.get(AppService);
20 | });
21 |
22 | describe('login', () => {
23 | it('should do something', () => {
24 | const appController = app.get(AppController);
25 | expect(appController.login({ email: 'test@test.com' })).toBeDefined();
26 | expect(appService.sendLoginEmail).toHaveBeenCalled();
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/apps/product/src/app/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, HttpCode, HttpStatus } from '@nestjs/common';
2 | import {
3 | ApiOkResponse,
4 | ApiOperation,
5 | ApiTags,
6 | } from '@nestjs/swagger';
7 | import { ProductDto } from '@projectx/models';
8 |
9 | import { AppService } from './app.service';
10 |
11 | @ApiTags('Product')
12 | @Controller()
13 | export class AppController {
14 | constructor(private readonly appService: AppService) {}
15 |
16 | /**
17 | * Endpoint to retrieve all available products.
18 | * @returns Array of ProductDto containing the product information.
19 | */
20 | @ApiOperation({
21 | summary: 'Get all products',
22 | description: 'This endpoint returns all available products',
23 | })
24 | @ApiOkResponse({
25 | description: 'Products retrieved successfully',
26 | type: ProductDto,
27 | isArray: true,
28 | })
29 | @Get()
30 | @HttpCode(HttpStatus.OK)
31 | getProducts() {
32 | return this.appService.getProducts();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule, ConfigService } from '@nestjs/config';
3 | import { JwtModule } from '@nestjs/jwt';
4 | import { PassportModule } from '@nestjs/passport';
5 |
6 | import { JwtAuthGuard } from './auth.guard';
7 | import { JwtStrategy } from './jwt.strategy';
8 | import { AuthService } from './auth.service';
9 |
10 | const passportModule = PassportModule.register({});
11 |
12 | @Module({
13 | imports: [
14 | passportModule,
15 | JwtModule.registerAsync({
16 | imports: [ConfigModule],
17 | useFactory: async (configService: ConfigService) => ({
18 | global: true,
19 | secret: configService.get('app.jwtSecret'),
20 | signOptions: { expiresIn: '12 days', algorithm: 'HS256' },
21 | }),
22 | inject: [ConfigService],
23 | }),
24 | ],
25 | providers: [JwtAuthGuard, JwtStrategy, AuthService],
26 | exports: [AuthService, passportModule],
27 | })
28 | export class AuthModule {}
29 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/configuration/validation.ts:
--------------------------------------------------------------------------------
1 | import { ClassConstructor, plainToInstance } from 'class-transformer';
2 | import { validateSync } from 'class-validator';
3 |
4 | /**
5 | * Validate the configuration object against the environment variables class.
6 | * It's used with the ConfigModule.forRoot() method.
7 | * @param config - The configuration object to validate.
8 | * @param envVariablesClass - The environment variables class to validate against.
9 | * @returns The validated configuration object.
10 | */
11 | export function validateConfiguration(
12 | config: Record,
13 | envVariablesClass: ClassConstructor
14 | ): T {
15 | const validatedConfig = plainToInstance(envVariablesClass, config, {
16 | enableImplicitConversion: true,
17 | });
18 | const errors = validateSync(validatedConfig as object, {
19 | skipMissingProperties: false,
20 | });
21 |
22 | if (errors.length > 0) {
23 | throw new Error(errors.toString());
24 | }
25 | return validatedConfig;
26 | }
27 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/swagger/setup.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { ConfigService } from '@nestjs/config';
3 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
4 | import { capitalize } from 'lodash';
5 |
6 | export function setupAppSwagger(app: INestApplication): void {
7 | const configService = app.get(ConfigService);
8 | const apiPrefix = configService.get('app.apiPrefix') ?? '';
9 | const title = configService.get('swagger.title');
10 | const description = configService.get('swagger.description');
11 | const version = configService.get('swagger.version');
12 |
13 | const config = new DocumentBuilder()
14 | .setTitle(title)
15 | .setDescription(description)
16 | .setVersion(version)
17 | .addTag(`${capitalize(apiPrefix)} API`)
18 | .addBearerAuth()
19 | .build();
20 | const document = SwaggerModule.createDocument(app, config, {
21 | ignoreGlobalPrefix: false,
22 | });
23 |
24 | SwaggerModule.setup('swagger', app, document);
25 | }
26 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/core.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule, ConfigService } from '@nestjs/config';
3 | import { Environment } from '@projectx/models';
4 | import { LoggerModule } from 'nestjs-pino';
5 |
6 | import { createLoggerOptions } from './logger';
7 | import { HealthModule } from './health';
8 | import { AuthModule } from './auth';
9 |
10 | @Module({
11 | imports: [
12 | HealthModule,
13 | LoggerModule.forRootAsync({
14 | imports: [ConfigModule],
15 | inject: [ConfigService],
16 | useFactory: async (configService: ConfigService) => {
17 | return {
18 | pinoHttp: createLoggerOptions(
19 | configService.get('app.logLevel') ?? 'info',
20 | configService.get('app.apiPrefix') ?? 'app',
21 | configService.get('app.environment') ?? Environment.Development
22 | ),
23 | };
24 | },
25 | }),
26 | AuthModule,
27 | ],
28 | exports: [AuthModule],
29 | })
30 | export class CoreModule {}
31 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/navigation/MobileNavigation.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 | import { MobileNavigation } from './MobileNavigation';
3 | import { withRouterProvider } from '../providers/router';
4 |
5 | const meta: Meta = {
6 | component: MobileNavigation,
7 | title: 'Navigation/Mobile',
8 | args: {
9 | sections: [
10 | {
11 | title: 'Home',
12 | links: [{ title: 'Home', href: '/' }],
13 | },
14 | {
15 | title: 'About',
16 | links: [{ title: 'About', href: '/about' }],
17 | },
18 | ],
19 | },
20 | };
21 | export default meta;
22 | type Story = StoryObj;
23 |
24 | const MobileNavigationWithProviders = withRouterProvider(MobileNavigation);
25 |
26 | export const Primary: Story = {
27 | render: (args) => (
28 |
29 |
30 |
31 | ),
32 | };
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "importHelpers": true,
11 | "target": "es2017",
12 | "module": "esnext",
13 | "lib": ["es2020", "dom"],
14 | "skipLibCheck": true,
15 | "skipDefaultLibCheck": true,
16 | "incremental": true,
17 | "baseUrl": ".",
18 | "paths": {
19 | "@projectx/core": ["libs/backend/core/src/index.ts"],
20 | "@projectx/db": ["libs/backend/db/src/index.ts"],
21 | "@projectx/email": ["libs/backend/email/src/index.ts"],
22 | "@projectx/models": ["libs/models/src/index.ts"],
23 | "@projectx/payment": ["libs/backend/payment/src/index.ts"],
24 | "@projectx/ui": ["libs/frontend/ui/src/index.ts"],
25 | "@projectx/workflows": ["libs/backend/workflows/src/index.ts"]
26 | }
27 | },
28 | "exclude": ["node_modules", "tmp"]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/types/events.ts:
--------------------------------------------------------------------------------
1 | import { Workflow } from './workflow';
2 |
3 | export type HandleErrorEventParams = {
4 | workflow: T;
5 | error?: Error;
6 | };
7 | export type HandleErrorEvent> = (
8 | params: HandleErrorEventParams
9 | ) => void;
10 |
11 | export type HandleRunEventParams = {
12 | workflow: T;
13 | };
14 | export type HandleRunEvent> = (
15 | params: HandleRunEventParams
16 | ) => void;
17 |
18 | export type HandleUpdateEventParams = {
19 | workflow: T;
20 | referenceId?: string;
21 | };
22 | export type HandleUpdateEvent> = (
23 | params: HandleUpdateEventParams
24 | ) => void;
25 |
26 | export type HandleUpsertEvent> = (params: {
27 | workflow: Partial;
28 | referenceId?: string;
29 | }) => void;
30 |
31 | export type HandleClearEventParams = {
32 | workflow: T;
33 | };
34 | export type HandleClearEvent> = (
35 | params: HandleClearEventParams
36 | ) => void;
37 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/logger/logger.ts:
--------------------------------------------------------------------------------
1 | import { Environment } from '@projectx/models';
2 | import { LoggerOptions } from 'pino';
3 | import { Request } from 'express';
4 | import { v4 as uuidv4 } from 'uuid';
5 |
6 | export function createLoggerOptions(
7 | level: string,
8 | serviceName: string,
9 | environment: Environment
10 | ) {
11 | const isProduction = environment === Environment.Production;
12 |
13 | return {
14 | level,
15 | name: serviceName,
16 | formatters: {
17 | level(label) {
18 | return { level: label };
19 | },
20 | },
21 | base: { service: serviceName },
22 | transport: !isProduction
23 | ? {
24 | target: 'pino-pretty',
25 | options: {
26 | colorize: true,
27 | translateTime: 'SYS:standard',
28 | ignore: 'pid,hostname',
29 | },
30 | }
31 | : undefined,
32 | // Generate a correlation ID for each request
33 | genReqId: (request: Request) =>
34 | request.headers['x-correlation-id'] || uuidv4(),
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/header/Header.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react';
2 |
3 | import { withRouterProvider } from '../providers/router';
4 | import { Header } from './Header';
5 |
6 | const meta: Meta = {
7 | component: Header,
8 | title: 'Header',
9 | args: {
10 | title: 'Home',
11 | sections: [
12 | {
13 | title: 'Home',
14 | links: [
15 | {
16 | title: 'Home',
17 | href: '/',
18 | },
19 | ],
20 | },
21 | {
22 | title: 'About',
23 | links: [
24 | {
25 | title: 'About',
26 | href: '/about',
27 | },
28 | ],
29 | },
30 | ],
31 | },
32 | };
33 | export default meta;
34 | type Story = StoryObj;
35 |
36 | const HeaderWithProviders = withRouterProvider(Header);
37 |
38 | export const Primary: Story = {
39 | render: (args) => (
40 |
41 |
42 |
43 | ),
44 | };
45 |
--------------------------------------------------------------------------------
/libs/models/src/validators/profanity.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Profanity } from '@2toad/profanity';
3 | import {
4 | registerDecorator,
5 | ValidationOptions,
6 | ValidatorConstraint,
7 | ValidatorConstraintInterface,
8 | } from 'class-validator';
9 |
10 | @Injectable()
11 | @ValidatorConstraint({ async: false })
12 | export class ProfanityValidator implements ValidatorConstraintInterface {
13 | private profanity: Profanity;
14 |
15 | constructor() {
16 | this.profanity = new Profanity();
17 | }
18 |
19 | validate(text: string) {
20 | return typeof text === 'string' && !this.profanity.exists(text);
21 | }
22 |
23 | defaultMessage() {
24 | return 'The provided text contains inappropriate words.';
25 | }
26 | }
27 |
28 | export function NoProfanity(validationOptions?: ValidationOptions) {
29 | return function (obj: object, propertyName: string) {
30 | registerDecorator({
31 | target: obj.constructor,
32 | propertyName: propertyName,
33 | options: validationOptions,
34 | constraints: [],
35 | validator: ProfanityValidator,
36 | });
37 | };
38 | }
39 |
--------------------------------------------------------------------------------
/libs/backend/workflows/src/lib/worker.ts:
--------------------------------------------------------------------------------
1 | import { ConfigService } from '@nestjs/config';
2 | import {
3 | bundleWorkflowCode,
4 | NativeConnection,
5 | WorkerOptions,
6 | } from '@temporalio/worker';
7 |
8 | /**
9 | * Creates a worker options object for the Temporal worker.
10 | * @param config - The NestJS configuration service.
11 | * @param workflowsPath - The path to the workflows directory.
12 | * @returns A Promise resolving to the worker options object.
13 | */
14 | export async function createWorkerOptions(
15 | config: ConfigService,
16 | workflowsPath: string
17 | ): Promise {
18 | const temporalHost = config.get('temporal.host');
19 | const temporalNamespace = config.get('temporal.namespace');
20 | const temporalTaskQueue = config.get('temporal.taskQueue');
21 | const connection = await NativeConnection.connect({
22 | address: temporalHost,
23 | // tls configuration
24 | });
25 |
26 | const workflowBundle = await bundleWorkflowCode({
27 | workflowsPath,
28 | });
29 |
30 | return {
31 | connection,
32 | namespace: temporalNamespace,
33 | taskQueue: temporalTaskQueue,
34 | workflowBundle,
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/libs/models/src/transforms.ts:
--------------------------------------------------------------------------------
1 | import {
2 | isString,
3 | isDefined,
4 | isDateString,
5 | isNumberString,
6 | } from 'class-validator';
7 |
8 | export function transformToLowerCase(value: string) {
9 | if (isDefined(value) && isString(value)) {
10 | return value.toLowerCase();
11 | }
12 | return value;
13 | }
14 |
15 | export function trimTransform(value: string) {
16 | if (isDefined(value) && isString(value)) {
17 | return value.trim();
18 | }
19 | return value;
20 | }
21 |
22 | export function transformToNumber(value: string) {
23 | if (isDefined(value) && isNumberString(value)) {
24 | const number = Number(value);
25 | if (isNaN(number)) {
26 | throw new Error(`Invalid number: ${value}`);
27 | }
28 | return number;
29 | }
30 | return value;
31 | }
32 |
33 | export function transformToDate(value: string, allowNull = true) {
34 | if (!value && allowNull) {
35 | return null;
36 | }
37 | if (isString(value) && !isDateString(value)) {
38 | return new Error(`Invalid format, expected ISO string: ${value}`);
39 | }
40 | const date = new Date(value);
41 | return isNaN(date.getTime()) ? new Error(`Invalid date: ${value}`) : date;
42 | }
43 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "lib": ["DOM", "DOM.Iterable", "ES2019"],
5 | "isolatedModules": true,
6 | "esModuleInterop": true,
7 | "jsx": "react-jsx",
8 | "moduleResolution": "Bundler",
9 | "resolveJsonModule": true,
10 | "target": "ES2022",
11 | "strict": true,
12 | "allowJs": true,
13 | "skipLibCheck": true,
14 | "skipDefaultLibCheck": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "baseUrl": ".",
17 | "paths": {
18 | "~/*": ["app/*"],
19 | "@projectx/models": ["../../libs/models/src/index.ts"],
20 | "@projectx/email": ["../../libs/backend/email/src/index.ts"],
21 | "@projectx/core/logger": ["../../libs/backend/core/src/lib/logger/logger.ts"],
22 | "@projectx/ui": ["../../libs/frontend/ui/src/index.ts"],
23 | "@projectx/ui/tailwind-config": ["../../libs/frontend/ui/tailwind.config.ts"],
24 | },
25 | // Remix takes care of building everything in `remix build`.
26 | "noEmit": true
27 | },
28 | "include": [],
29 | "files": [],
30 | "references": [
31 | {
32 | "path": "./tsconfig.app.json"
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/libs/models/src/auth/auth.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { IsJWT, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
3 | import { Expose, Type } from 'class-transformer';
4 |
5 | import { AuthLoginDto } from './login.dto';
6 | import { UserDto } from '../user';
7 |
8 | export class AuthPayload extends AuthLoginDto {
9 | @ApiProperty({
10 | description: 'User ID',
11 | type: String,
12 | example: '1',
13 | })
14 | @Expose()
15 | @IsNotEmpty()
16 | sub!: string;
17 |
18 | @ApiProperty({
19 | description: 'Username',
20 | type: String,
21 | example: 'user123',
22 | })
23 | @Expose()
24 | @IsString()
25 | username?: string;
26 | }
27 |
28 | export class AuthResponseDto {
29 | @ApiProperty({
30 | description: 'Access token',
31 | type: String,
32 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjI5MjIwNjI5LCJleHAiOjE2MjkzMDcxMjl9.1'
33 | })
34 | @IsJWT()
35 | @IsNotEmpty()
36 | accessToken!: string;
37 |
38 | @ApiProperty({
39 | description: 'User information',
40 | type: UserDto,
41 | })
42 | @ValidateNested()
43 | @Type(() => UserDto)
44 | @IsNotEmpty()
45 | user!: UserDto;
46 | }
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/auth/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { ConfigService } from '@nestjs/config';
3 | import { PassportStrategy } from '@nestjs/passport';
4 | import { AuthPayload } from '@projectx/models';
5 | import { ExtractJwt, Strategy } from 'passport-jwt';
6 | import { AuthUser } from '../user';
7 |
8 | /**
9 | * JwtStrategy is passport JWT strategy.
10 | *
11 | * @export
12 | * @class JwtStrategy
13 | * @extends {PassportStrategy(Strategy)}
14 | */
15 | @Injectable()
16 | export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
17 | constructor(configService: ConfigService) {
18 | super({
19 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
20 | ignoreExpiration: false,
21 | secretOrKey: configService.get('app.jwtSecret'),
22 | });
23 | }
24 |
25 | /**
26 | * validate returns jwt payload.
27 | * @param payload - Payload with the info of the user
28 | *
29 | * @returns
30 | * @memberof JwtStrategy
31 | */
32 | async validate(payload: AuthPayload) {
33 | return {
34 | id: Number(payload.sub),
35 | email: payload.email,
36 | username: payload.username,
37 | } as AuthUser;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/apps/web/app/hooks/useProducts.ts:
--------------------------------------------------------------------------------
1 | import type { ProductDto } from '@projectx/models';
2 | import { useInfiniteQuery } from '@tanstack/react-query';
3 | import axios from 'axios';
4 |
5 | const PRODUCTS_QUERY_KEY = 'products';
6 | const MAX_RETRY_ATTEMPTS = 3;
7 |
8 | export const useProducts = ({
9 | initialData = [] as ProductDto[],
10 | size = 10,
11 | }) => {
12 | return useInfiniteQuery({
13 | queryKey: [PRODUCTS_QUERY_KEY],
14 | queryFn: async ({ pageParam = 1 }) => {
15 | // TODO: Use limit and offset to load more products from the endpoint
16 | const response = await axios.get(`${window.ENV.PRODUCT_API_URL}/product`);
17 | return response.data;
18 | },
19 | enabled: true,
20 | refetchOnWindowFocus: true,
21 | retry: (failureCount) => failureCount <= MAX_RETRY_ATTEMPTS,
22 | getNextPageParam: (lastPage: ProductDto[], pages: ProductDto[][]) => {
23 | if (lastPage.length === size) {
24 | return pages.length + 1;
25 | }
26 | return undefined;
27 | },
28 | initialPageParam: 1,
29 | ...(!!initialData?.length && {
30 | initialData: {
31 | pages: [initialData],
32 | pageParams: [null],
33 | },
34 | }),
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/store/types.ts:
--------------------------------------------------------------------------------
1 | import { Reducer, Dispatch } from 'react';
2 |
3 | import { Workflow, WorkflowType } from '../types';
4 |
5 | export enum StoreActions {
6 | RunWorkflow,
7 | ClearWorkflow,
8 | UpdateWorkflow,
9 | UpsertWorkflow,
10 | }
11 |
12 | export type StoreState = {
13 | workflows: Record>>;
14 | };
15 |
16 | export type StoreAction =
17 | | {
18 | type: StoreActions.RunWorkflow;
19 | payload: Workflow;
20 | workflowType: WorkflowType;
21 | }
22 | | {
23 | type: StoreActions.ClearWorkflow;
24 | referenceId: Workflow['referenceId'];
25 | workflowType: WorkflowType;
26 | }
27 | | {
28 | type: StoreActions.UpdateWorkflow;
29 | payload: Workflow;
30 | workflowType: WorkflowType;
31 | referenceId: Workflow['referenceId'];
32 | }
33 | | {
34 | type: StoreActions.UpsertWorkflow;
35 | payload: Partial>;
36 | workflowType: WorkflowType;
37 | referenceId: Workflow['referenceId'];
38 | };
39 |
40 | export type StoreReducer = Reducer;
41 | export type StoreDispatch = Dispatch;
42 |
43 | export type ContextProps = [StoreState, StoreDispatch];
44 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | permissions:
10 | actions: read
11 | contents: read
12 |
13 | jobs:
14 | main:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v4
18 | with:
19 | fetch-depth: 0
20 |
21 |
22 |
23 | # This enables task distribution via Nx Cloud
24 | # Run this command as early as possible, before dependencies are installed
25 | # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
26 | - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build"
27 |
28 |
29 | # Cache node_modules
30 | - uses: actions/setup-node@v4
31 | with:
32 | node-version: 20
33 | cache: 'npm'
34 |
35 | - run: npm ci --legacy-peer-deps
36 | - uses: nrwl/nx-set-shas@v4
37 |
38 | # Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
39 | # - run: npx nx-cloud record -- echo Hello World
40 | # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
41 | - run: npx nx affected -t lint test build
--------------------------------------------------------------------------------
/deployment/config/dynamicconfig/development.yaml:
--------------------------------------------------------------------------------
1 | frontend.enableUpdateWorkflowExecution:
2 | - value: true
3 | constraints: {}
4 | frontend.enableClientVersionCheck:
5 | - value: true
6 | constraints: {}
7 | history.persistenceMaxQPS:
8 | - value: 3000
9 | constraints: {}
10 | frontend.persistenceMaxQPS:
11 | - value: 3000
12 | constraints: {}
13 | frontend.historyMgrNumConns:
14 | - value: 10
15 | constraints: {}
16 | frontend.throttledLogRPS:
17 | - value: 20
18 | constraints: {}
19 | history.historyMgrNumConns:
20 | - value: 50
21 | constraints: {}
22 | history.defaultActivityRetryPolicy:
23 | - value:
24 | InitialIntervalInSeconds: 1
25 | MaximumIntervalCoefficient: 100.0
26 | BackoffCoefficient: 2.0
27 | MaximumAttempts: 0
28 | history.defaultWorkflowRetryPolicy:
29 | - value:
30 | InitialIntervalInSeconds: 1
31 | MaximumIntervalCoefficient: 100.0
32 | BackoffCoefficient: 2.0
33 | MaximumAttempts: 0
34 | system.advancedVisibilityWritingMode:
35 | - value: "off"
36 | constraints: {}
37 | limit.maxIDLength:
38 | - value: 255
39 | constraints: {}
40 | system.forceSearchAttributesCacheRefreshOnRead:
41 | - value: true # Dev setup only. Please don't turn this on in production.
42 | constraints: {}
--------------------------------------------------------------------------------
/libs/models/src/role/role.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { Expose, Transform } from 'class-transformer';
3 | import {
4 | IsDate,
5 | IsDefined,
6 | IsInt,
7 | IsOptional,
8 | IsString,
9 | MaxLength,
10 | } from 'class-validator';
11 |
12 | import { trimTransform } from '../transforms';
13 |
14 | export class RoleDto {
15 | constructor(partial?: Partial) {
16 | Object.assign(this, partial);
17 | }
18 |
19 | @ApiProperty({ description: 'Unique identifier for the role' })
20 | @IsInt()
21 | @IsDefined()
22 | @Expose()
23 | id!: number;
24 |
25 | @ApiProperty({ description: 'Name of the role' })
26 | @IsString()
27 | @Expose()
28 | @MaxLength(100)
29 | @Transform(({ value }) => trimTransform(value))
30 | name!: string;
31 |
32 | @ApiProperty({ description: 'Description of the role' })
33 | @IsString()
34 | @Expose()
35 | @Transform(({ value }) => trimTransform(value))
36 | @IsOptional()
37 | description?: string;
38 |
39 | @ApiProperty({ description: 'Date the role was created' })
40 | @IsDefined()
41 | @IsDate()
42 | @Expose()
43 | createdAt!: Date;
44 |
45 | @ApiProperty({ description: 'Date the role was last updated' })
46 | @IsDefined()
47 | @IsDate()
48 | @Expose()
49 | updatedAt!: Date;
50 | }
51 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/services/order.ts:
--------------------------------------------------------------------------------
1 | import type { CreateOrderDto, OrderStatusResponseDto, OrderCreateResponseDto } from '@projectx/models';
2 | import axios from 'axios';
3 |
4 | export async function createOrder(
5 | accessToken: string,
6 | orderDto: CreateOrderDto,
7 | ): Promise {
8 | const response = await axios.post(
9 | `${window.ENV.ORDER_API_URL}/order`,
10 | orderDto,
11 | {
12 | headers: {
13 | Authorization: `Bearer ${accessToken}`,
14 | },
15 | },
16 | );
17 | return response.data;
18 | }
19 |
20 | export async function getOrderStatus(
21 | accessToken: string,
22 | referenceId: string,
23 | ): Promise {
24 | const response = await axios.get(
25 | `${window.ENV.ORDER_API_URL}/order/${referenceId}`,
26 | {
27 | headers: {
28 | Authorization: `Bearer ${accessToken}`,
29 | },
30 | },
31 | );
32 | return response.data;
33 | }
34 |
35 | export async function cancelOrder(
36 | accessToken: string,
37 | referenceId: string,
38 | ): Promise {
39 | await axios.delete(`${window.ENV.ORDER_API_URL}/order/${referenceId}`, {
40 | headers: {
41 | Authorization: `Bearer ${accessToken}`,
42 | },
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/libs/backend/core/src/lib/user/workflow.utils.ts:
--------------------------------------------------------------------------------
1 | import type { UserDto } from '@projectx/models';
2 | import { defineQuery, defineUpdate } from '@temporalio/workflow';
3 |
4 | export type LoginWorkflowData = {
5 | email: string;
6 | };
7 |
8 | export enum LoginWorkflowCodeStatus {
9 | PENDING = 'PENDING',
10 | SENT = 'SENT',
11 | }
12 |
13 | export enum LoginWorkflowStatus {
14 | PENDING = 'PENDING',
15 | SUCCESS = 'SUCCESS',
16 | FAILED = 'FAILED',
17 | }
18 |
19 | export type LoginWorkflowState = {
20 | codeStatus: LoginWorkflowCodeStatus;
21 | user?: UserDto;
22 | status: LoginWorkflowStatus;
23 | code?: string;
24 | };
25 |
26 | export enum LoginWorkflowRetryableErrors {
27 | VERIFY_LOGIN_CODE_FAILURE = 'VERIFY_LOGIN_CODE_FAILURE',
28 | }
29 |
30 | export enum LoginWorkflowNonRetryableErrors {
31 | UNKNOWN_ERROR = 'UNKNOWN_ERROR_NON_RETRY',
32 | LOGIN_CODE_EXPIRED = 'LOGIN_CODE_EXPIRED',
33 | }
34 |
35 | // DEFINE QUERIES
36 | /**
37 | * Get the login state
38 | */
39 | export const getLoginStateQuery = defineQuery(
40 | 'getLoginStateQuery'
41 | );
42 |
43 | // DEFINE UPDATES
44 | /**
45 | * Verify the login code
46 | */
47 | export const verifyLoginCodeUpdate = defineUpdate<
48 | {
49 | user?: UserDto;
50 | },
51 | [number]
52 | >('verifyLoginCodeUpdate');
53 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/buttons/theme/ThemeButton.tsx:
--------------------------------------------------------------------------------
1 | import { MoonIcon } from '@heroicons/react/20/solid';
2 | import { SunIcon } from '@heroicons/react/24/outline';
3 |
4 | import { classnames } from '../../../utils';
5 |
6 | export interface ThemeButtonProps {
7 | theme: string;
8 | className?: string;
9 | onChange?: (theme: string) => void;
10 | }
11 |
12 | export function ThemeButton({ theme, onChange, className }: ThemeButtonProps) {
13 | const toggleTheme = (e: React.ChangeEvent) => {
14 | const isChecked = e.target.checked;
15 | const newTheme = isChecked ? 'dark' : 'light';
16 | document.documentElement.setAttribute('data-theme', newTheme);
17 | onChange?.(newTheme);
18 | };
19 |
20 | return (
21 |
38 | );
39 | }
40 |
41 | export default ThemeButton;
42 |
--------------------------------------------------------------------------------
/libs/frontend/ui/src/lib/icons/GitHub.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type IconProps = {
4 | className?: string;
5 | color?: string;
6 | };
7 |
8 | export const GitHubIcon: React.FC = ({
9 | className,
10 | color = 'currentColor'
11 | }) => {
12 | return (
13 |
24 | );
25 | };
26 |
27 | export default GitHubIcon;
28 |
--------------------------------------------------------------------------------
/libs/backend/email/src/lib/auth/login.ts:
--------------------------------------------------------------------------------
1 | import { COLORS, FONT_SIZES, FONT_WEIGHTS } from '../common/constants';
2 | import { getEmailHtml } from '../common/mjml';
3 |
4 | export type LoginEmailData = {
5 | token: string;
6 | userName: string;
7 | };
8 |
9 | export function getLoginEmailHtml(data: T): string {
10 | const body = `
11 |
12 |
13 |
14 | Let's sign you up
15 |
16 |
17 | ${['Hi', data.userName].filter(Boolean).join(', ')}
18 |
19 |
20 | Here is the security code to verify your account
21 |
22 |
23 | ${data.token}
24 |
25 |
26 | This code will expire in 20 minutes.
27 |
28 |
29 |
30 | `;
31 | return getEmailHtml(body);
32 | }
33 |
--------------------------------------------------------------------------------
/apps/web/app/providers/workflows/types/workflow.ts:
--------------------------------------------------------------------------------
1 | export enum WorkflowTypes {
2 | ORDER = 'order',
3 | // Add other workflows here...
4 | }
5 |
6 | export type WorkflowType = WorkflowTypes | `${WorkflowTypes}`;
7 | export enum WorkflowStep {
8 | START = 'start',
9 | STATUS = 'status',
10 | CANCEL = 'cancel',
11 | }
12 |
13 | export interface Workflow {
14 | // The email is used to filter the workflows by the current user
15 | email?: string;
16 | // The referenceId is used to identify the workflow in the backend
17 | referenceId: string;
18 | // We use the step to identify the current state of the workflow and check if the workflow is completed
19 | step: WorkflowStep | `${WorkflowStep}`;
20 | // The data includes the data/state of the workflow from the backend
21 | data: T;
22 | // The error indicates if there was an error running this process
23 | error?: Error;
24 | // Determines if the workflow was created from the backend and there's a process running
25 | isInitialized?: boolean;
26 | // Can can request for a cancellation of the workflow, this flag is used to determine if the workflow is being canceled
27 | isCanceling?: boolean;
28 | // The below properties are used to manage the expiration of the workflow
29 | retries?: number;
30 | maxRetries?: number;
31 | expirationTimeInMilliseconds?: number;
32 | startDate?: number;
33 | }
34 |
--------------------------------------------------------------------------------
/prompts/thinking.md:
--------------------------------------------------------------------------------
1 | You are an AI assistant designed to provide detailed, step-by-step responses. Your outputs should follow this structure:
2 |
3 | 1. Begin with a section.
4 | 2. Inside the thinking section:
5 | a. Briefly analyze the question and outline your approach.
6 | b. Present a clear plan of steps to solve the problem.
7 | c. Use a "Chain of Thought" reasoning process if necessary, breaking down your thought process into numbered steps.
8 | 3. Include a section for each idea where you:
9 | a. Review your reasoning.
10 | b. Check for potential errors or oversights.
11 | c. Confirm or adjust your conclusion if necessary.
12 | 4. Be sure to close all reflection sections.
13 | 5. Close the thinking section with .
14 | 6. Provide your final answer in an