parent
fd79d99444
commit
b5af543a0e
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__dirname),
|
||||||
|
});
|
||||||
@ -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"]
|
||||||
|
}
|
||||||
@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Using fixtures to represent data",
|
||||||
|
"email": "hello@cypress.io"
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const getGreeting = () => cy.get('h1');
|
||||||
@ -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<Subject> {
|
||||||
|
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) => { ... })
|
||||||
@ -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';
|
||||||
@ -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"]
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
NX_STRAPI_URL_API=http://localhost:1337/api
|
||||||
|
NX_STRAPI_URL=http://localhost:1337
|
||||||
|
NEXTAUTH_URL=http://localhost:4300
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 (<p>header </p>)
|
||||||
|
} else {
|
||||||
|
return ('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const renderFooters = () => {
|
||||||
|
if (showFooter) {
|
||||||
|
return (<p>footer </p>)
|
||||||
|
} else {
|
||||||
|
return ('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderHeaders()}
|
||||||
|
<div
|
||||||
|
className={'dark:bg-gray-900 bg-gray-50 ' + styles['app-container'] + ' ' + (showHeaders ? '' : styles['without-headers']) + ' ' + (showFooter ? '' : styles['without-footer']) + ' ' + className}>
|
||||||
|
<div className={styles['app-content'] + " w-full mx-auto"}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
{renderFooters()}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
@ -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 <html> 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'
|
||||||
|
}
|
||||||
@ -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",
|
||||||
|
}
|
||||||
@ -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",
|
||||||
|
}
|
||||||
@ -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",
|
||||||
|
}
|
||||||
@ -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",
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
declare module '*.svg' {
|
||||||
|
const content: any;
|
||||||
|
export const ReactComponent: any;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
@ -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',
|
||||||
|
};
|
||||||
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 = <T>(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)];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||||
@ -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);
|
||||||
@ -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 (
|
||||||
|
<>
|
||||||
|
<SessionProvider session={pageProps.session}>
|
||||||
|
<ThemeProvider enableSystem={true}
|
||||||
|
attribute="class">
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</ThemeProvider>
|
||||||
|
</SessionProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomApp;
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import {Head, Html, Main, NextScript} from 'next/document';
|
||||||
|
|
||||||
|
export default function Document() {
|
||||||
|
return (
|
||||||
|
<Html lang='en'>
|
||||||
|
<Head/>
|
||||||
|
<body>
|
||||||
|
<Main/>
|
||||||
|
<NextScript/>
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
)
|
||||||
|
};
|
||||||
@ -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);
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
.page {
|
||||||
|
}
|
||||||
@ -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 (
|
||||||
|
<div className={styles.page}>
|
||||||
|
<Layout>
|
||||||
|
<p>home page !</p>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
||||||
@ -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'
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* Replace this with your own classes
|
||||||
|
*
|
||||||
|
* e.g.
|
||||||
|
* .container {
|
||||||
|
* }
|
||||||
|
*/
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
|
/* eslint-disable-next-line */
|
||||||
|
export interface LoginProps {}
|
||||||
|
|
||||||
|
export function SignIn(props: LoginProps) {
|
||||||
|
return (
|
||||||
|
<div className={styles['container']}>
|
||||||
|
<h1>Welcome to Login!</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SignIn;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind components;
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind utilities;
|
||||||
@ -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: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -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": []
|
||||||
|
}
|
||||||
@ -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(<Index />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -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: [],
|
||||||
|
};
|
||||||
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in new issue