diff --git a/apps/vendors-e2e/.eslintrc.json b/apps/vendors-e2e/.eslintrc.json new file mode 100644 index 0000000..696cb8b --- /dev/null +++ b/apps/vendors-e2e/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/vendors-e2e/cypress.config.ts b/apps/vendors-e2e/cypress.config.ts new file mode 100644 index 0000000..22f7c84 --- /dev/null +++ b/apps/vendors-e2e/cypress.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'cypress'; +import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; + +export default defineConfig({ + e2e: nxE2EPreset(__dirname), +}); diff --git a/apps/vendors-e2e/project.json b/apps/vendors-e2e/project.json new file mode 100644 index 0000000..8fbdf8d --- /dev/null +++ b/apps/vendors-e2e/project.json @@ -0,0 +1,30 @@ +{ + "name": "vendors-e2e", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/vendors-e2e/src", + "projectType": "application", + "targets": { + "e2e": { + "executor": "@nrwl/cypress:cypress", + "options": { + "cypressConfig": "apps/vendors-e2e/cypress.config.ts", + "devServerTarget": "vendors:serve:development", + "testingType": "e2e" + }, + "configurations": { + "production": { + "devServerTarget": "vendors:serve:production" + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/vendors-e2e/**/*.{js,ts}"] + } + } + }, + "tags": [], + "implicitDependencies": ["vendors"] +} diff --git a/apps/vendors-e2e/src/e2e/app.cy.ts b/apps/vendors-e2e/src/e2e/app.cy.ts new file mode 100644 index 0000000..5b600a7 --- /dev/null +++ b/apps/vendors-e2e/src/e2e/app.cy.ts @@ -0,0 +1,13 @@ +import { getGreeting } from '../support/app.po'; + +describe('vendors', () => { + beforeEach(() => cy.visit('/')); + + it('should display welcome message', () => { + // Custom command example, see `../support/commands.ts` file + cy.login('my-email@something.com', 'myPassword'); + + // Function helper example, see `../support/app.po.ts` file + getGreeting().contains('Welcome vendors'); + }); +}); diff --git a/apps/vendors-e2e/src/fixtures/example.json b/apps/vendors-e2e/src/fixtures/example.json new file mode 100644 index 0000000..294cbed --- /dev/null +++ b/apps/vendors-e2e/src/fixtures/example.json @@ -0,0 +1,4 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io" +} diff --git a/apps/vendors-e2e/src/support/app.po.ts b/apps/vendors-e2e/src/support/app.po.ts new file mode 100644 index 0000000..3293424 --- /dev/null +++ b/apps/vendors-e2e/src/support/app.po.ts @@ -0,0 +1 @@ +export const getGreeting = () => cy.get('h1'); diff --git a/apps/vendors-e2e/src/support/commands.ts b/apps/vendors-e2e/src/support/commands.ts new file mode 100644 index 0000000..310f1fa --- /dev/null +++ b/apps/vendors-e2e/src/support/commands.ts @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + } +} +// +// -- This is a parent command -- +Cypress.Commands.add('login', (email, password) => { + console.log('Custom command example: Login', email, password); +}); +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/vendors-e2e/src/support/e2e.ts b/apps/vendors-e2e/src/support/e2e.ts new file mode 100644 index 0000000..3d469a6 --- /dev/null +++ b/apps/vendors-e2e/src/support/e2e.ts @@ -0,0 +1,17 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; diff --git a/apps/vendors-e2e/tsconfig.json b/apps/vendors-e2e/tsconfig.json new file mode 100644 index 0000000..cc509a7 --- /dev/null +++ b/apps/vendors-e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "sourceMap": false, + "outDir": "../../dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"] + }, + "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] +} diff --git a/apps/vendors/.env b/apps/vendors/.env new file mode 100644 index 0000000..00f79ff --- /dev/null +++ b/apps/vendors/.env @@ -0,0 +1,3 @@ +NX_STRAPI_URL_API=http://localhost:1337/api +NX_STRAPI_URL=http://localhost:1337 +NEXTAUTH_URL=http://localhost:4300 diff --git a/apps/vendors/.eslintrc.json b/apps/vendors/.eslintrc.json new file mode 100644 index 0000000..860885e --- /dev/null +++ b/apps/vendors/.eslintrc.json @@ -0,0 +1,31 @@ +{ + "extends": [ + "plugin:@nrwl/nx/react-typescript", + "next", + "next/core-web-vitals", + "../../.eslintrc.json" + ], + "ignorePatterns": ["!**/*", ".next/**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@next/next/no-html-link-for-pages": ["error", "apps/vendors/pages"] + } + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ], + "rules": { + "@next/next/no-html-link-for-pages": "off" + }, + "env": { + "jest": true + } +} diff --git a/apps/vendors/components/layout/layout.module.scss b/apps/vendors/components/layout/layout.module.scss new file mode 100644 index 0000000..69603f2 --- /dev/null +++ b/apps/vendors/components/layout/layout.module.scss @@ -0,0 +1,71 @@ +/* + * Replace this with your own classes + * + * e.g. + * .container { + * } +*/ +.app-container { + position: relative; + min-height: 100vh; + + .app-content { + position: relative; + padding-top: 58px; + min-height: calc(100vh - 189px); + } +} + +.without-headers { + padding-top: 0; + + .app-content { + padding-top: 0; + } +} + +.without-footer { + padding-top: 0; + + .app-content { + min-height: 100vh; + } +} + +@media (min-width: 453px) { + .app-container:not(.without-footer) { + .app-content { + min-height: calc(100vh - 169px); + } + } +} + +@media (min-width: 640px) { + .app-container:not(.without-footer) { + .app-content { + min-height: calc(100vh - 133px); + } + } +} + +@media (min-width: 768px) { + .app-container:not(.without-headers) { + .app-content { + padding-top: 128px; + } + } + .app-container:not(.without-footer) { + .app-content { + min-height: calc(100vh - 165px); + } + } +} + +@media (min-width: 1024px) { + .app-container:not(.without-footer) { + .app-content { + padding-top: 131px; + min-height: calc(100vh - 181px); + } + } +} diff --git a/apps/vendors/components/layout/layout.tsx b/apps/vendors/components/layout/layout.tsx new file mode 100644 index 0000000..9bd2add --- /dev/null +++ b/apps/vendors/components/layout/layout.tsx @@ -0,0 +1,43 @@ + +import styles from './layout.module.scss'; + +export function Layout({ + showHeaders = true, + showFooter = true, + menuHeader = [], + menuFooter = [], + children = null, + className = '', + headerTransparent = false + }) { + + const renderHeaders = () => { + if (showHeaders) { + return (

header

) + } else { + return (''); + } + } + const renderFooters = () => { + if (showFooter) { + return (

footer

) + } else { + return (''); + } + } + + return ( + <> + {renderHeaders()} +
+
+ {children} +
+ {renderFooters()} +
+ + ); +} + +export default Layout; diff --git a/apps/vendors/config.ts b/apps/vendors/config.ts new file mode 100644 index 0000000..2d6eede --- /dev/null +++ b/apps/vendors/config.ts @@ -0,0 +1,50 @@ +export const config = { + env: { + STRAPI_URL_API: process.env.NX_STRAPI_URL_API, + STRAPI_URL: process.env.NX_STRAPI_URL, + NEXTAUTH_SECRET: process.env.NX_NEXTAUTH_SECRET, + NEXTAUTH_URL: process.env.NX_NEXTAUTH_URL, + }, + mainApiEndpoint: 'https://conduit.productionready.io/api' +} + +export const theme = { + colors: { + primary: '#3b5998', + secondary: '#8b9dc3', + gray: '#f7f7f7' + }, + width: { + xl: 1200, + l: 900, + m: 750, + s: 400 + } +} + +export const siteConfig = { + siteTitle: 'Conduit', // Navigation and Site Title + siteUrl: process.env.ROOT_URL || 'https://next.org', // Domain of your site. No trailing slash! + siteLanguage: 'fr', // Language Tag on element + siteLogo: '/logo.png', // Used for SEO and manifest, path to your image you placed in the 'static' folder + siteDescription: 'Real world example of nextjs SSR', + author: 'arrlancore', // Author for schemaORGJSONLD + organization: 'Open Source Organization', + + userTwitter: '@arrlancore', // Twitter Username + ogSiteName: 'Dwiki Arlan', // Facebook Site Name + ogLanguage: 'en_US', + googleAnalyticsID: process.env.GOOGLE_ANALYTIC_ID || 'UA-XXX123-1', + + // Manifest and Progress color + themeColor: '#4147DC', + backgroundColor: '#231C42', + defaultUserAvatar: '', + + // Social component + twitter: 'https://twitter.com/arrlancore/', + twitterHandle: '@arrlancore', + github: 'https://github.com/arrlancore/', + youtube: 'https://www.youtube.com/', + rss: 'https://next.org/blog/rss.xml' +} diff --git a/apps/vendors/environments/environment.development.ts b/apps/vendors/environments/environment.development.ts new file mode 100644 index 0000000..c4b1e9a --- /dev/null +++ b/apps/vendors/environments/environment.development.ts @@ -0,0 +1,8 @@ +export const environment = { + appUrl: 'http://localhost:4200', + strapiUrl: 'http://localhost:1337', + strapiApiUrl: 'http://localhost:1337/api', + nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP", + meiliUrl: "http://127.0.0.1:7700", + meiliApiKey: "f2e963e883cbacf6d4f63e792dc276491b8a866fb837f3fda37b8b78889a6851", +} diff --git a/apps/vendors/environments/environment.prod.ts b/apps/vendors/environments/environment.prod.ts new file mode 100644 index 0000000..84758a1 --- /dev/null +++ b/apps/vendors/environments/environment.prod.ts @@ -0,0 +1,8 @@ +export const environment = { + appUrl: 'http://localhost:4200', + strapiUrl: 'https://admin.mecp.nasercloud.fr', + strapiApiUrl: 'https://admin.mecp.nasercloud.fr/api', + nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP", + meiliUrl: "https://ms-b6dafcb0b382-1943.fra.meilisearch.io", + meiliApiKey: "0e9a9d027b9e6b2d98c9670e5030a8d1aa72cab911fd768d4ad02636ae673690", +} diff --git a/apps/vendors/environments/environment.staging.ts b/apps/vendors/environments/environment.staging.ts new file mode 100644 index 0000000..1f0f314 --- /dev/null +++ b/apps/vendors/environments/environment.staging.ts @@ -0,0 +1,8 @@ +export const environment = { + appUrl: 'http://localhost:4200', + strapiUrl: 'https://admin.mecp.nasercloud.fr', + strapiApiUrl: 'https://admin.mecp.nasercloud.fr/api', + nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP", + meiliUrl: "https://ms-b6dafcb0b382-1943.fra.meilisearch.io", + meiliApiKey: "dda9e4a354839db9ae18c3722ae75422515c06525b3a841010928b5256f54e72", +} diff --git a/apps/vendors/environments/environment.ts b/apps/vendors/environments/environment.ts new file mode 100644 index 0000000..520e2bd --- /dev/null +++ b/apps/vendors/environments/environment.ts @@ -0,0 +1,8 @@ +export const environment = { + appUrl: 'http://localhost:4200', + strapiUrl: 'http://localhost:1337', + strapiApiUrl: 'http://localhost:1337/api', + nextAuthSecret: "yBNW1wrb9CgOvEfbX6EQCvCDqiMkRBZP", + meiliUrl: "http://127.0.0.1:7700", + meiliApiKey: "dda9e4a354839db9ae18c3722ae75422515c06525b3a841010928b5256f54e72", +} diff --git a/apps/vendors/index.d.ts b/apps/vendors/index.d.ts new file mode 100644 index 0000000..7ba08fa --- /dev/null +++ b/apps/vendors/index.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +declare module '*.svg' { + const content: any; + export const ReactComponent: any; + export default content; +} diff --git a/apps/vendors/jest.config.ts b/apps/vendors/jest.config.ts new file mode 100644 index 0000000..2b25a49 --- /dev/null +++ b/apps/vendors/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'vendors', + preset: '../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/apps/vendors', +}; diff --git a/apps/vendors/libs/api.ts b/apps/vendors/libs/api.ts new file mode 100644 index 0000000..a19166a --- /dev/null +++ b/apps/vendors/libs/api.ts @@ -0,0 +1,46 @@ +import axios from "axios"; +import * as _ from 'lodash'; + +import {environment} from "../environments/environment"; +import {config} from "../config"; + +export type Inputs = { + email: string, + username: string, + password: string, + passwordConfirmation: string, + agreement: boolean, + newsletter: boolean, +} + +export type LostPasswordInputs = { + email: string; +} + +export type ResetPasswordInputs = { + password: string, + passwordConfirmation: string, + code: string +} + +export const hasAvatar = (user) => { + return !_.isNil(user.avatar); +} + +export const getBackendImg = (imageUrl: string) => { + return `${environment.strapiUrl}${imageUrl}`; +} + +export const signUpRequest = async (inputs: Inputs) => { + try { + const registerResponse = await axios.post(`${environment.strapiApiUrl}/auth/local/register`, { + email: inputs.email, + password: inputs.password, + username: inputs.username, + newsletter: inputs.newsletter + }); + return registerResponse.data; + } catch (err) { + return Promise.reject('Internal error'); + } +} diff --git a/apps/vendors/libs/auth.ts b/apps/vendors/libs/auth.ts new file mode 100644 index 0000000..9091c12 --- /dev/null +++ b/apps/vendors/libs/auth.ts @@ -0,0 +1,50 @@ +import axios from 'axios'; +import {signIn} from "next-auth/react"; +import * as _ from 'lodash'; + +import {config} from "../config"; +import {environment} from "../environments/environment"; + +export async function createJwtToken(email, password) { + return await signIn('credentials', { + redirect: false, + email, + password, + }); +} + +export async function signInStrapi({email, password}) { + const res = await axios.post(`${environment.strapiApiUrl}/auth/local`, { + identifier: email, + password, + }); + if (res && res.data) { + const user = await axios.get(`${environment.strapiApiUrl}/users/${res.data.user.id}?populate=deep`); + if (user && user.data) { + res.data.user = {...res.data.user, ...user.data}; + } + } + return res.data; +} + +export async function lostPasswordStrapi({email}) { + const res = await axios.post(`${environment.strapiApiUrl}/auth/forgot-password`, {email}); + if (res && res.data) { + return res.data.ok; + } else { + return false; + } +} + +export async function resetPasswordStrapi({password, passwordConfirmation, code}) { + const res = await axios.post(`${environment.strapiApiUrl}/auth/reset-password`, { + password, + passwordConfirmation, + code + }); + if (res && res.data) { + return !_.isNil(res.data.jwt); + } else { + return false; + } +} diff --git a/apps/vendors/libs/utils.ts b/apps/vendors/libs/utils.ts new file mode 100644 index 0000000..57fcf34 --- /dev/null +++ b/apps/vendors/libs/utils.ts @@ -0,0 +1,132 @@ +import delve from 'dlv'; +import {ImageLoader, ImageLoaderProps} from "next/image"; +import {isEmpty, isEqual} from "radash"; + +import {environment} from "../environments/environment"; + +export type ImageFormatType = "default" | "thumbnail" | "medium" | "small"; + +export interface PriceRange { + min: number; + max: number; +} + +export type SortType = 'pas' | 'pde' | 'alp'; + +export interface QueryParam { + param: string; + value: PriceRange | string | number[]; +} + +export const buildQueriesListFromQueryParams = (query): QueryParam[] => { + const queryMap = ['sort', 'cat', 'page', 'facets', 'filters', 'search']; + + return queryMap.map((param: string) => { + if (!isEmpty(query[param])) { + let value = !isEmpty(query[param]) ? query[param] : null; + + if (param === 'filters') { + value = query[param].split(',').map((value: string) => parseInt(value, 10)); + } + + if (param === 'facets') { + const values = query[param].split(':'); + value = {min: parseInt(values[0], 10), max: parseInt(values[1], 10)}; + } + + return ({param, value}); + } else { + return null; + } + }).filter((val) => !isEmpty(val)); +} + +export const findQueryParamsValue = (queryList: Array<{ param: string, value: T }>, params: string): T | null => { + const paramGET = queryList.find((val: { param: string, value: T }) => val.param === params); + return !isEmpty(paramGET) ? paramGET.value : null; +} + +export const getShopSortList = (): Array<{ value: SortType, label: string }> => { + return [ + {value: "pas", label: 'Prix croissant'}, + {value: "pde", label: 'Prix décroissant'}, + {value: "alp", label: 'Ordre alphabetique'}, + ]; +} + +export const buildShopParamsGET = (sort: SortType = 'pas', + page: number = null, + range: PriceRange = { + min: 0, + max: 0 + }, + cat: string = null, + filters: number[] = [], + search: string) => { + let url = `?sort=${sort}`; + + if (!isEmpty(range) && !isEqual(range, {min: 0, max: 0})) { + url += `&facets=${range.min}:${range.max}`; + } + if (!isEmpty(filters)) { + url += `&filters=${filters.join(',')}`; + } + if (!isEmpty(cat)) { + url += `&cat=${cat}`; + } + if (!isEmpty(page)) { + url += `&page=${page}`; + } + if (!isEmpty(search)) { + url += `&search=${search}`; + } + + return url; +} + +export const contentfulImageLoader: ImageLoader = ({src, width}: ImageLoaderProps) => { + return `${src}?w=${width}` +} + +export const getCategoryUrl = (item): string => { + const categorySlug = !delve(item, 'attributes', null) ? delve(item, 'category.data.attributes.slug', '') : delve(item, 'attributes.category.data.attributes.slug', ''); + return '/blog/' + categorySlug; +} + +export const getPostUrl = (item): string => { + const categorySlug = !delve(item, 'attributes', null) ? delve(item, 'category.data.attributes.slug', '') : delve(item, 'attributes.category.data.attributes.slug', ''); + const postSlug = !delve(item, 'attributes', null) ? delve(item, 'slug', '') : delve(item, 'attributes.slug', ''); + return '/blog/' + categorySlug + '/' + postSlug; +} + +export const getStrapiImage = (item, format: ImageFormatType = 'default') => { + const image = !delve(item, 'attributes', null) ? delve(item, 'image.data.attributes', {}) : delve(item, 'attributes.image.data.attributes', {}); + switch (format) { + case "default": + return environment.strapiUrl + delve(image, "url", "/images/default.png"); + case "medium": + return environment.strapiUrl + delve(image, "formats.medium.url", "/images/default.png"); + case "small": + return environment.strapiUrl + delve(image, "formats.small.url", "/images/default.png"); + case "thumbnail": + return environment.strapiUrl + delve(image, "formats.thumbnail.url", "/images/default.png"); + default: + return environment.strapiUrl + delve(image, "url", "/images/default.png"); + } +} + +export const getStrapiImageSize = (item, format: ImageFormatType = 'default'): [number, number] => { + const image = delve(item, 'attributes.image.data.attributes', {}); + switch (format) { + case "default": + return [delve(image, "width", 100), delve(image, "height", 100)]; + case "medium": + return [delve(image, "formats.medium.width", 100), delve(image, "formats.medium.height", 100)]; + case "small": + return [delve(image, "formats.small.width", 100), delve(image, "formats.small.height", 100)]; + case "thumbnail": + return [delve(image, "formats.thumbnail.width", 100), delve(image, "formats.thumbnail.height", 100)]; + default: + return [delve(image, "width", 100), delve(image, "height", 100)]; + } +} diff --git a/apps/vendors/next-env.d.ts b/apps/vendors/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/apps/vendors/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/vendors/next.config.js b/apps/vendors/next.config.js new file mode 100644 index 0000000..4405045 --- /dev/null +++ b/apps/vendors/next.config.js @@ -0,0 +1,36 @@ +//@ts-check + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { withNx } = require('@nrwl/next/plugins/with-nx'); + +/** + * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions} + **/ +const nextConfig = { + reactStrictMode: true, + poweredByHeader: false, + output: 'standalone', + compiler: { + styledComponents: true + }, + nx: { + // Set this to true if you would like to to use SVGR + // See: https://github.com/gregberge/svgr + svgr: false + }, + images: { + loader: "default", + remotePatterns: [ + { + protocol: 'https', + hostname: 'mcep.nasercloud.fr', + }, { + protocol: 'https', + hostname: 'admin.mcep.nasercloud.fr', + } + ], + domains: ["localhost", "127.0.0.1", "admin.mcep.nasercloud.fr", "mcep.nasercloud.fr"], + } +}; + +module.exports = withNx(nextConfig); diff --git a/apps/vendors/pages/_app.tsx b/apps/vendors/pages/_app.tsx new file mode 100644 index 0000000..4e1efe9 --- /dev/null +++ b/apps/vendors/pages/_app.tsx @@ -0,0 +1,21 @@ +import App, {AppProps} from 'next/app'; +import {ThemeProvider} from "next-themes"; +import {SessionProvider} from "next-auth/react"; + +import './styles.css'; + +function CustomApp({Component, pageProps}: AppProps) { + pageProps = {...pageProps}; + return ( + <> + + + + + + + ); +} + +export default CustomApp; diff --git a/apps/vendors/pages/_document.tsx b/apps/vendors/pages/_document.tsx new file mode 100644 index 0000000..8175832 --- /dev/null +++ b/apps/vendors/pages/_document.tsx @@ -0,0 +1,13 @@ +import {Head, Html, Main, NextScript} from 'next/document'; + +export default function Document() { + return ( + + + +
+ + + + ) +}; diff --git a/apps/vendors/pages/api/auth/[...nextauth].ts b/apps/vendors/pages/api/auth/[...nextauth].ts new file mode 100644 index 0000000..8a79976 --- /dev/null +++ b/apps/vendors/pages/api/auth/[...nextauth].ts @@ -0,0 +1,69 @@ +import NextAuth, {AuthOptions, Session, User} from 'next-auth'; +import CredentialsProvider from 'next-auth/providers/credentials'; +import {JWT} from "next-auth/jwt"; +import {AdapterUser} from "next-auth/adapters"; + +import {environment} from "../../../environments/environment"; +import {signInStrapi} from '../../../libs/auth'; + +export const authOptions: AuthOptions = { + pages: { + signIn: '/sign-in' + }, + // Configure one or more authentication providers + providers: [ + CredentialsProvider({ + name: 'Sign in with Email', + credentials: { + email: {label: 'Email', type: 'text'}, + password: {label: 'Password', type: 'password'}, + }, + async authorize(credentials, req) { + /** + * This function is used to define if the user is authenticated or not. + * If authenticated, the function should return an object contains the user data. + * If not, the function should return `null`. + */ + if (credentials == null) return null; + /** + * credentials is defined in the config above. + * We can expect it contains two properties: `email` and `password` + */ + try { + const {user, jwt} = await signInStrapi({ + email: credentials.email, + password: credentials.password, + }); + return {...user, jwt}; + } catch (error) { + // Sign In Fail + return null; + } + }, + }), + ], + callbacks: { + session: async ({session, token}: { session: Session, token: JWT }) => { + (session as any).id = token.id; + (session as any).jwt = token.jwt; + (session as any).user = { + avatar: (token.user as any).avatar, + username: (token.user as any).username, + email: (token.user as any).email, + }; + return Promise.resolve(session); + }, + jwt: async ({token, user}: { token: JWT, user: User | AdapterUser }) => { + const isSignIn = user ? true : false; + if (isSignIn) { + token.id = user.id; + token.jwt = (user as any).jwt; + token.user = (user as any); + } + return Promise.resolve(token); + }, + }, + secret: environment.nextAuthSecret +} + +export default NextAuth(authOptions); diff --git a/apps/vendors/pages/index.module.scss b/apps/vendors/pages/index.module.scss new file mode 100644 index 0000000..8a13e21 --- /dev/null +++ b/apps/vendors/pages/index.module.scss @@ -0,0 +1,2 @@ +.page { +} diff --git a/apps/vendors/pages/index.tsx b/apps/vendors/pages/index.tsx new file mode 100644 index 0000000..e74ec39 --- /dev/null +++ b/apps/vendors/pages/index.tsx @@ -0,0 +1,19 @@ +import styles from './index.module.scss'; +import Layout from "../components/layout/layout"; + +export function Index() { + /* + * Replace the elements below with your own. + * + * Note: The corresponding styles are in the ./index.scss file. + */ + return ( +
+ +

home page !

+
+
+ ); +} + +export default Index; diff --git a/apps/vendors/pages/middleware.ts b/apps/vendors/pages/middleware.ts new file mode 100644 index 0000000..4cbd69d --- /dev/null +++ b/apps/vendors/pages/middleware.ts @@ -0,0 +1,18 @@ +import {withAuth} from "next-auth/middleware"; + +export default withAuth({ + callbacks: { + authorized: async ({req}) => { + const pathname = req.nextUrl.pathname; + + if (pathname.startsWith('/_next') || pathname === '/favicon.ico' || pathname === '/__ENV.js') { + return true + } + + return false + }, + }, + pages: { + signIn: '/sign-in' + } +}) diff --git a/apps/vendors/pages/sign-in/index.module.scss b/apps/vendors/pages/sign-in/index.module.scss new file mode 100644 index 0000000..45c2aa4 --- /dev/null +++ b/apps/vendors/pages/sign-in/index.module.scss @@ -0,0 +1,7 @@ +/* + * Replace this with your own classes + * + * e.g. + * .container { + * } +*/ diff --git a/apps/vendors/pages/sign-in/index.tsx b/apps/vendors/pages/sign-in/index.tsx new file mode 100644 index 0000000..c9e73d2 --- /dev/null +++ b/apps/vendors/pages/sign-in/index.tsx @@ -0,0 +1,14 @@ +import styles from './index.module.scss'; + +/* eslint-disable-next-line */ +export interface LoginProps {} + +export function SignIn(props: LoginProps) { + return ( +
+

Welcome to Login!

+
+ ); +} + +export default SignIn; diff --git a/apps/vendors/pages/styles.css b/apps/vendors/pages/styles.css new file mode 100644 index 0000000..23d597f --- /dev/null +++ b/apps/vendors/pages/styles.css @@ -0,0 +1,3 @@ +@tailwind components; +@tailwind base; +@tailwind utilities; diff --git a/apps/vendors/postcss.config.js b/apps/vendors/postcss.config.js new file mode 100644 index 0000000..c72626d --- /dev/null +++ b/apps/vendors/postcss.config.js @@ -0,0 +1,15 @@ +const { join } = require('path'); + +// Note: If you use library-specific PostCSS/Tailwind configuration then you should remove the `postcssConfig` build +// option from your application's configuration (i.e. project.json). +// +// See: https://nx.dev/guides/using-tailwind-css-in-react#step-4:-applying-configuration-to-libraries + +module.exports = { + plugins: { + tailwindcss: { + config: join(__dirname, 'tailwind.config.js'), + }, + autoprefixer: {}, + }, +}; diff --git a/apps/vendors/project.json b/apps/vendors/project.json new file mode 100644 index 0000000..4a962f3 --- /dev/null +++ b/apps/vendors/project.json @@ -0,0 +1,64 @@ +{ + "name": "vendors", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/vendors", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/next:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "root": "apps/vendors", + "outputPath": "dist/apps/vendors" + }, + "configurations": { + "development": { + "outputPath": "apps/vendors" + }, + "production": {} + } + }, + "serve": { + "executor": "@nrwl/next:server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "vendors:build", + "dev": true, + "port": 4300 + }, + "configurations": { + "development": { + "buildTarget": "vendors:build:development", + "dev": true + }, + "production": { + "buildTarget": "vendors:build:production", + "dev": false + } + } + }, + "export": { + "executor": "@nrwl/next:export", + "options": { + "buildTarget": "vendors:build:production" + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/vendors/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/vendors/**/*.{ts,tsx,js,jsx}"] + } + } + }, + "tags": [] +} diff --git a/apps/vendors/public/.gitkeep b/apps/vendors/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/vendors/specs/index.spec.tsx b/apps/vendors/specs/index.spec.tsx new file mode 100644 index 0000000..42c9402 --- /dev/null +++ b/apps/vendors/specs/index.spec.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import Index from '../pages/index'; + +describe('Index', () => { + it('should render successfully', () => { + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); +}); diff --git a/apps/vendors/tailwind.config.js b/apps/vendors/tailwind.config.js new file mode 100644 index 0000000..d00839f --- /dev/null +++ b/apps/vendors/tailwind.config.js @@ -0,0 +1,17 @@ +const { createGlobPatternsForDependencies } = require('@nrwl/react/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join( + __dirname, + '{src,pages,components}/**/*!(*.stories|*.spec).{ts,tsx,html}' + ), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/vendors/tsconfig.json b/apps/vendors/tsconfig.json new file mode 100644 index 0000000..7a9f3ec --- /dev/null +++ b/apps/vendors/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "preserve", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "resolveJsonModule": true, + "isolatedModules": true, + "incremental": true, + "types": ["jest", "node"] + }, + "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], + "exclude": [ + "node_modules", + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ] +} diff --git a/apps/vendors/tsconfig.spec.json b/apps/vendors/tsconfig.spec.json new file mode 100644 index 0000000..214b2cc --- /dev/null +++ b/apps/vendors/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "jsx": "react" + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/package-lock.json b/package-lock.json index eaea6c9..6c5b678 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2671,9 +2671,9 @@ } }, "@panva/hkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz", - "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.4.tgz", + "integrity": "sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ==" }, "@parcel/watcher": { "version": "2.0.4", @@ -9447,9 +9447,9 @@ } }, "jose": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz", - "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==" + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.13.0.tgz", + "integrity": "sha512-v6BN7fuPVfG9XIxcPT2jzyAg5EmA/mtNeJEXJ7d31Wz7fFOqOZeN8mPtNJYQmnuAIxJII7EcURcbZ7qXs9a4kA==" }, "js-tokens": { "version": "4.0.0", @@ -10185,9 +10185,9 @@ } }, "next-auth": { - "version": "4.18.8", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.18.8.tgz", - "integrity": "sha512-USP8ihmvB7iCGtkS0+toe2QPrzdbZfkydQZX56JOI9Ft5n/BardOXh3D4wQ2An+vpq/jDKojGlgfv21wVElW7A==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.19.2.tgz", + "integrity": "sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ==", "requires": { "@babel/runtime": "^7.16.3", "@panva/hkdf": "^1.0.1", @@ -10532,9 +10532,9 @@ "dev": true }, "openid-client": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.1.tgz", - "integrity": "sha512-RLfehQiHch9N6tRWNx68cicf3b1WR0x74bJWHRc25uYIbSRwjxYcTFaRnzbbpls5jroLAaB/bFIodTgA5LJMvw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.0.tgz", + "integrity": "sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==", "requires": { "jose": "^4.10.0", "lru-cache": "^6.0.0", @@ -11192,9 +11192,9 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "preact": { - "version": "10.11.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", - "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==" + "version": "10.13.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.13.0.tgz", + "integrity": "sha512-ERdIdUpR6doqdaSIh80hvzebHB7O6JxycOhyzAeLEchqOq/4yueslQbfnPwXaNhAYacFTyCclhwkEbOumT0tHw==" }, "preact-render-to-string": { "version": "5.2.6", diff --git a/package.json b/package.json index fd0834c..31ba2c7 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "build": "nx build", "test": "nx test", "start:website": "nx serve website", + "start:vendors": "nx serve vendors", "build:website:development": "nx build website --configuration development --verbose", "build:website:staging": "nx build website --configuration staging", "postbuild:website:staging": "node scripts/website/build-staging.js", @@ -26,7 +27,7 @@ "flowbite-react": "^0.3.8", "meilisearch": "^0.31.1", "next": "13.1.1", - "next-auth": "4.18.8", + "next-auth": "4.19.2", "next-redux-wrapper": "^8.1.0", "next-themes": "^0.2.1", "radash": "^10.7.0",