commit
eb612e0bf2
@ -1,7 +1,10 @@
|
||||
export default {
|
||||
responses: {
|
||||
privateAttributes: ['_v', 'id', 'created_at'],
|
||||
},
|
||||
rest: {
|
||||
defaultLimit: 25,
|
||||
maxLimit: 100,
|
||||
defaultLimit: 100,
|
||||
maxLimit: 250,
|
||||
withCount: true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,11 +1,22 @@
|
||||
import path from 'path';
|
||||
|
||||
export default ({env}) => ({
|
||||
connection: {
|
||||
client: 'sqlite',
|
||||
client: 'postgres',
|
||||
connection: {
|
||||
filename: path.join(__dirname, '..', '..', env('DATABASE_FILENAME', '.tmp/data.db')),
|
||||
host: env('DATABASE_HOST', 'localhost'),
|
||||
port: env.int('DATABASE_PORT', 5432),
|
||||
database: env('DATABASE_NAME', 'bank'),
|
||||
user: env('DATABASE_USERNAME', 'postgres'),
|
||||
password: env('DATABASE_PASSWORD', '0000'),
|
||||
schema: env('DATABASE_SCHEMA', 'public'), // Not required
|
||||
ssl: false,
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
debug: false,
|
||||
},
|
||||
// connection: {
|
||||
// client: 'sqlite',
|
||||
// connection: {
|
||||
// filename: path.join(__dirname, '..', '..', env('DATABASE_FILENAME', '.tmp/data.db')),
|
||||
// },
|
||||
// useNullAsDefault: true,
|
||||
// },
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
||||
services:
|
||||
gs_postgres:
|
||||
image: postgres:14-alpine
|
||||
container_name: gs_postgres
|
||||
shm_size: '4gb'
|
||||
environment:
|
||||
POSTGRES_USER: ${DATABASE_USERNAME}
|
||||
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
|
||||
POSTGRES_DB: ${DATABASE_NAME}
|
||||
TZ: Europe/Paris
|
||||
healthcheck:
|
||||
test: 'PGPASSWORD="${DATABASE_PASSWORD}" psql --host ${DATABASE_HOST} --username ${DATABASE_USERNAME} --dbname ${DATABASE_NAME} -c "select 1" ; [ "0" -eq "$$?" ]; echo $$?'
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
volumes:
|
||||
- gs_postgres-master:/var/lib/postgresql/data:z
|
||||
- ./.db/init:/docker-entrypoint-initdb.d
|
||||
ports:
|
||||
- ${DATABASE_PORT}:5432 # DB
|
||||
@ -0,0 +1,28 @@
|
||||
{
|
||||
"kind": "collectionType",
|
||||
"collectionName": "pages",
|
||||
"info": {
|
||||
"singularName": "page",
|
||||
"pluralName": "pages",
|
||||
"displayName": "Pages",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": true
|
||||
},
|
||||
"pluginOptions": {},
|
||||
"attributes": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "uid",
|
||||
"targetField": "label"
|
||||
},
|
||||
"seo": {
|
||||
"type": "component",
|
||||
"repeatable": false,
|
||||
"component": "shared.seo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* page controller
|
||||
*/
|
||||
|
||||
import { factories } from '@strapi/strapi'
|
||||
|
||||
export default factories.createCoreController('api::page.page');
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* page router
|
||||
*/
|
||||
|
||||
import { factories } from '@strapi/strapi';
|
||||
|
||||
export default factories.createCoreRouter('api::page.page');
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* page service
|
||||
*/
|
||||
|
||||
import { factories } from '@strapi/strapi';
|
||||
|
||||
export default factories.createCoreService('api::page.page');
|
||||
@ -0,0 +1,16 @@
|
||||
{
|
||||
"collectionName": "components_meta_metas",
|
||||
"info": {
|
||||
"displayName": "meta",
|
||||
"icon": "network-wired"
|
||||
},
|
||||
"options": {},
|
||||
"attributes": {
|
||||
"property": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
{
|
||||
"collectionName": "components_shared_seos",
|
||||
"info": {
|
||||
"displayName": "Seo",
|
||||
"icon": "book",
|
||||
"description": ""
|
||||
},
|
||||
"options": {},
|
||||
"attributes": {
|
||||
"metaTitle": {
|
||||
"type": "string"
|
||||
},
|
||||
"metaDescription": {
|
||||
"type": "string"
|
||||
},
|
||||
"SharedImage": {
|
||||
"displayName": "SharedImage",
|
||||
"type": "component",
|
||||
"repeatable": false,
|
||||
"component": "shared.shared-image"
|
||||
},
|
||||
"Meta": {
|
||||
"displayName": "meta",
|
||||
"type": "component",
|
||||
"repeatable": true,
|
||||
"component": "meta.meta"
|
||||
},
|
||||
"preventIndexing": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"structuredData": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"collectionName": "components_shared_shared_images",
|
||||
"info": {
|
||||
"displayName": "SharedImage",
|
||||
"icon": "file-image",
|
||||
"description": ""
|
||||
},
|
||||
"options": {},
|
||||
"attributes": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"media": {
|
||||
"allowedTypes": [
|
||||
"images",
|
||||
"files",
|
||||
"videos",
|
||||
"audios"
|
||||
],
|
||||
"type": "media",
|
||||
"multiple": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
STRAPI_URL=http://127.0.0.1:1337/api
|
||||
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Carousel from './carousel';
|
||||
|
||||
describe('Carousel', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Carousel />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
import styles from './carousel.module.scss';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface CarouselProps {}
|
||||
|
||||
export function Carousel(props: CarouselProps) {
|
||||
return (
|
||||
<div className={styles['container']}>
|
||||
<h1>Welcome to Carousel!</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Carousel;
|
||||
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Footer from './footer';
|
||||
|
||||
describe('Footer', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Footer />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,36 @@
|
||||
import delve from "dlv";
|
||||
|
||||
export function Footer({items = []}) {
|
||||
|
||||
const generateItem = (item, index) => {
|
||||
const value = delve(item, 'attributes', {});
|
||||
|
||||
return (
|
||||
<li key={value.title + index}>
|
||||
<a href={value.url} target={value.target} className="mr-4 hover:underline md:mr-6 ">
|
||||
{value.title}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<footer className="p-4 bg-white rounded-lg shadow md:px-6 md:py-8 dark:bg-gray-900">
|
||||
<div className="sm:flex sm:items-center sm:justify-between">
|
||||
<a href="https://flowbite.com/" className="flex items-center mb-4 sm:mb-0">
|
||||
<img src="https://flowbite.com/docs/images/logo.svg" className="h-8 mr-3" alt="Flowbite Logo"/>
|
||||
<span className="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">Flowbite</span>
|
||||
</a>
|
||||
<ul className="flex flex-wrap items-center mb-6 text-sm text-gray-500 sm:mb-0 dark:text-gray-400">
|
||||
{items.map((item, index: number) => generateItem(item, index))}
|
||||
</ul>
|
||||
</div>
|
||||
<hr className="my-6 border-gray-200 sm:mx-auto dark:border-gray-700 lg:my-8"/>
|
||||
<span className="block text-sm text-gray-500 sm:text-center dark:text-gray-400">© 3 <a
|
||||
href="https://flowbite.com/" className="hover:underline">Flowbite™</a>. All Rights Reserved.
|
||||
</span>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Header from './header';
|
||||
|
||||
describe('Header', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Header />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,152 @@
|
||||
import {Collapse, Dropdown} from "flowbite";
|
||||
import {useEffect} from "react";
|
||||
import delve from "dlv";
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface AppHeaderProps {
|
||||
}
|
||||
|
||||
export function Header({items = []}) {
|
||||
useEffect(function mount() {
|
||||
const burgerButton = document.getElementById('mega-menu-button');
|
||||
const megaZone = document.getElementById('mega-menu');
|
||||
new Collapse(megaZone, burgerButton);
|
||||
|
||||
const userButton = document.getElementById('user-menu-button');
|
||||
const userZone = document.getElementById('user-dropdown');
|
||||
new Dropdown(userZone, userButton);
|
||||
});
|
||||
|
||||
const generateItem = (item, index, isActive = false) => {
|
||||
const value = delve(item, 'attributes', {});
|
||||
return (
|
||||
<li key={value.title + index}>
|
||||
{value.children.data.length > 0 ? dropdownItem(value, isActive) : regularItem(value, isActive)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const regularItem = (item, isActive = false) => {
|
||||
return (
|
||||
<a href={item.url}
|
||||
target={item.target}
|
||||
className={(isActive ? 'text-blue-600' : '') + " block py-2 pl-3 pr-4 border-b border-gray-100 hover:bg-gray-50 md:hover:bg-transparent md:border-0 md:hover:text-blue-600 md:p-0 dark:text-blue-500 md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-blue-500 md:dark:hover:bg-transparent dark:border-gray-700"}
|
||||
aria-current="page">{item.title}</a>
|
||||
)
|
||||
}
|
||||
|
||||
const regularDropdownItem = (item, index) => {
|
||||
const value = delve(item, 'attributes', {});
|
||||
return (
|
||||
<li key={value.title + index}>
|
||||
<a href={value.url}
|
||||
className="dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-500">
|
||||
{value.title}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const dropdownItem = (item, isActive = false) => {
|
||||
return (
|
||||
<>
|
||||
<button id="mega-menu-dropdown-button"
|
||||
data-dropdown-toggle="mega-menu-dropdown"
|
||||
className={(isActive ? 'text-blue-600' : '') + " flex items-center justify-between w-full py-2 pl-3 pr-4 font-medium border-b border-gray-100 md:w-auto hover:bg-gray-50 md:hover:bg-transparent md:border-0 md:hover:text-blue-600 md:p-0 dark:text-gray-400 md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-blue-500 md:dark:hover:bg-transparent dark:border-gray-700"}>
|
||||
{item.title}
|
||||
<svg aria-hidden="true"
|
||||
className="w-5 h-5 ml-1 md:w-4 md:h-4"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="mega-menu-dropdown"
|
||||
className="absolute z-10 grid hidden w-auto text-sm bg-white border border-gray-100 rounded-lg shadow-md dark:border-gray-700 dark:bg-gray-700">
|
||||
<div className="p-4 pb-0 text-gray-900 md:pb-4 dark:text-white">
|
||||
<ul className="space-y-4" aria-labelledby="mega-menu-dropdown-button">
|
||||
{item.children.data.map((children, index) => regularDropdownItem(children, index))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="bg-white px-2 sm:px-4 py-2.5 dark:bg-gray-900 fixed w-full z-20 top-0 left-0 border-b border-gray-200 dark:border-gray-600">
|
||||
<div className="container flex flex-wrap items-center justify-between mx-auto">
|
||||
<a href="https://flowbite.com" className="flex items-center">
|
||||
<img src="https://flowbite.com/docs/images/logo.svg" className="h-6 mr-3 sm:h-9" alt="Flowbite Logo"/>
|
||||
<span className="self-center text-xl font-semibold whitespace-nowrap dark:text-white">Flowbite</span>
|
||||
</a>
|
||||
<div className="flex items-center md:order-2">
|
||||
<a href="#"
|
||||
className="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 py-2 md:px-5 md:py-2.5 mr-1 md:mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Login</a>
|
||||
<a href="#"
|
||||
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 md:px-5 md:py-2.5 mr-1 md:mr-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Sign
|
||||
up</a>
|
||||
|
||||
<button type="button"
|
||||
className="flex mr-3 text-sm bg-gray-800 rounded-full md:mr-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||
id="user-menu-button" aria-expanded="false" data-dropdown-toggle="user-dropdown"
|
||||
data-dropdown-placement="bottom">
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<img className="w-8 h-8 rounded-full" src="/docs/images/people/profile-picture-3.jpg" alt="user photo"/>
|
||||
</button>
|
||||
<div
|
||||
className="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600"
|
||||
id="user-dropdown">
|
||||
<div className="px-4 py-3">
|
||||
<span className="block text-sm text-gray-900 dark:text-white">Bonnie Green</span>
|
||||
<span
|
||||
className="block text-sm font-medium text-gray-500 truncate dark:text-gray-400">name@flowbite.com</span>
|
||||
</div>
|
||||
<ul className="py-1" aria-labelledby="user-menu-button">
|
||||
<li>
|
||||
<a href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Dashboard</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Settings</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Earnings</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Sign
|
||||
out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button id="mega-menu-button" data-collapse-toggle="mega-menu" type="button"
|
||||
className="inline-flex items-center p-2 ml-1 text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
||||
aria-controls="mega-menu" aria-expanded="false">
|
||||
<span className="sr-only">Open main menu</span>
|
||||
<svg aria-hidden="true" className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fillRule="evenodd"
|
||||
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
||||
clipRule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="mega-menu" className="items-center justify-between hidden w-full md:flex md:w-auto md:order-1">
|
||||
<ul className="flex flex-col mt-4 font-medium md:flex-row md:space-x-8 md:mt-0">
|
||||
{items.map((item, index: number) => generateItem(item, index))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
.app-container {
|
||||
position: relative;
|
||||
padding-top: 70px;
|
||||
height: calc(100% - 181px);
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Layout from './layout';
|
||||
|
||||
describe('Layout', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Layout />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import Header from "../header/header";
|
||||
import Footer from "../footer/footer";
|
||||
|
||||
import styles from './layout.module.scss';
|
||||
|
||||
export function Layout({menuHeader = [], menuFooter = [], children}) {
|
||||
return (
|
||||
<>
|
||||
<Header items={menuHeader}/>
|
||||
<div className={styles['app-container'] + ' max-w-lg mx-auto'}>
|
||||
{children}
|
||||
</div>
|
||||
<Footer items={menuFooter}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import SeoConfig from './seo-config';
|
||||
|
||||
describe('SeoConfig', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<SeoConfig />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,69 @@
|
||||
import delve from "dlv";
|
||||
|
||||
import {siteConfig} from "../../config";
|
||||
import Head from "next/head";
|
||||
|
||||
export const SeoConfig = ({metaDescription = null, lang = null, meta = [], metaTitle = null, SharedImage = null}) => {
|
||||
const description = metaDescription || siteConfig.siteDescription;
|
||||
const title = metaTitle || siteConfig.siteTitle;
|
||||
const metaImage = SharedImage ? delve(SharedImage, `media.data.attributes.formats.small.url`, siteConfig.siteLogo) : siteConfig.siteLogo;
|
||||
const metaLang = lang || siteConfig.siteLanguage;
|
||||
const metas = [
|
||||
{
|
||||
name: `description`,
|
||||
content: metaDescription
|
||||
},
|
||||
{
|
||||
property: `og:title`,
|
||||
content: metaTitle
|
||||
},
|
||||
{
|
||||
property: `og:description`,
|
||||
content: metaDescription
|
||||
},
|
||||
{
|
||||
property: `og:image`,
|
||||
content: metaImage
|
||||
},
|
||||
{
|
||||
property: `og:type`,
|
||||
content: 'website'
|
||||
},
|
||||
{
|
||||
name: `twitter:card`,
|
||||
content: `summary_large_image`
|
||||
},
|
||||
{
|
||||
name: `twitter:creator`,
|
||||
content: siteConfig.author
|
||||
},
|
||||
{
|
||||
name: `twitter:title`,
|
||||
content: metaTitle
|
||||
},
|
||||
{
|
||||
name: `twitter:description`,
|
||||
content: metaDescription
|
||||
|
||||
}
|
||||
];
|
||||
const metaList = meta && meta.length > 0 ? meta : metas;
|
||||
const type = 'website';
|
||||
|
||||
return (
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
<meta name="description"
|
||||
content={description}/>
|
||||
<meta property="og:title"
|
||||
content={title}/>
|
||||
<meta property="lang"
|
||||
content={lang}/>
|
||||
{metaList.map((meta, index) => (
|
||||
<meta key={meta + index} property={meta.name} content={meta.content}/>
|
||||
))}
|
||||
</Head>
|
||||
);
|
||||
}
|
||||
|
||||
export default SeoConfig;
|
||||
@ -0,0 +1,44 @@
|
||||
export default {
|
||||
env: process.env.NODE_ENV,
|
||||
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',
|
||||
|
||||
// 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,37 @@
|
||||
import axios, {AxiosRequestConfig} from "axios";
|
||||
|
||||
export class ServerApiRequest {
|
||||
|
||||
private _axiosConfig: AxiosRequestConfig = {};
|
||||
|
||||
async getSeoMetadata(slug: string): Promise<any> {
|
||||
try {
|
||||
const response = await axios.get(`${process.env.STRAPI_URL}/pages?slug=${slug}&populate=deep`);
|
||||
console.log(response);
|
||||
if (response && response.data) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error('no data');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async getMenu(): Promise<any> {
|
||||
try {
|
||||
const response = await axios.get(`${process.env.STRAPI_URL}/menus/1?nested&populate=deep`);
|
||||
|
||||
if (response && response.data) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error('no data');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const serverApiRequest = new ServerApiRequest();
|
||||
export default serverApiRequest;
|
||||
@ -1,400 +1,15 @@
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif,
|
||||
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
border-radius: 0.25rem;
|
||||
color: rgba(229, 231, 235, 1);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
overflow: scroll;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
@tailwind components;
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
|
||||
.shadow {
|
||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
padding-bottom: 3rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
width: 100%;
|
||||
}
|
||||
#welcome {
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
#welcome h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1;
|
||||
}
|
||||
#welcome span {
|
||||
display: block;
|
||||
font-size: 1.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#hero {
|
||||
align-items: center;
|
||||
background-color: hsla(214, 62%, 21%, 1);
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#hero .text-container {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
padding: 3rem 2rem;
|
||||
}
|
||||
#hero .text-container h2 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
|
||||
html, body {
|
||||
position: relative;
|
||||
}
|
||||
#hero .text-container h2 svg {
|
||||
color: hsla(162, 47%, 50%, 1);
|
||||
height: 2rem;
|
||||
left: -0.25rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 2rem;
|
||||
}
|
||||
#hero .text-container h2 span {
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
#hero .text-container a {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0.75rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: inline-block;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 2rem;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
#hero .logo-container svg {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
width: 66.666667%;
|
||||
}
|
||||
#middle-content {
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
gap: 4rem;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#learning-materials {
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#learning-materials h2 {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.list-item-link {
|
||||
align-items: center;
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.list-item-link svg:first-child {
|
||||
margin-right: 1rem;
|
||||
height: 1.5rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1.5rem;
|
||||
}
|
||||
.list-item-link > span {
|
||||
flex-grow: 1;
|
||||
font-weight: 400;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link > span > span {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 300;
|
||||
line-height: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link svg:last-child {
|
||||
height: 1rem;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1rem;
|
||||
}
|
||||
.list-item-link:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: hsla(162, 47%, 50%, 1);
|
||||
}
|
||||
.list-item-link:hover > span {
|
||||
}
|
||||
.list-item-link:hover > span > span {
|
||||
color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
.list-item-link:hover svg:last-child {
|
||||
transform: translateX(0.25rem);
|
||||
}
|
||||
#other-links {
|
||||
}
|
||||
.button-pill {
|
||||
padding: 1.5rem 2rem;
|
||||
transition-duration: 300ms;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.button-pill svg {
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
.button-pill > span {
|
||||
letter-spacing: -0.025em;
|
||||
font-weight: 400;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.button-pill span span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.button-pill:hover svg,
|
||||
.button-pill:hover {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
#nx-console:hover {
|
||||
background-color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-console svg {
|
||||
color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-repo:hover {
|
||||
background-color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-repo svg {
|
||||
color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-cloud {
|
||||
margin-bottom: 2rem;
|
||||
margin-top: 2rem;
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#nx-cloud > div {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#nx-cloud > div svg {
|
||||
border-radius: 0.375rem;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
#nx-cloud > div h2 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#nx-cloud > div h2 span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
#nx-cloud p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud pre {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud a {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 1.5rem;
|
||||
text-align: right;
|
||||
}
|
||||
#nx-cloud a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#commands {
|
||||
padding: 2.5rem 2rem;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#commands h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#commands p {
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
details {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
details pre > span {
|
||||
color: rgba(181, 181, 181, 1);
|
||||
display: block;
|
||||
}
|
||||
summary {
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
summary:hover {
|
||||
background-color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
summary svg {
|
||||
height: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
#love {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 3.5rem;
|
||||
opacity: 0.6;
|
||||
text-align: center;
|
||||
}
|
||||
#love svg {
|
||||
color: rgba(252, 165, 165, 1);
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
display: inline;
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
#hero {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: flex;
|
||||
}
|
||||
#middle-content {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
body #__next {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@ -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,20 @@
|
||||
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),
|
||||
"./node_modules/flowbite-react/**/*.js",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('flowbite/plugin')
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,25 @@
|
||||
services:
|
||||
|
||||
# PostgreSQL (Database)
|
||||
gs_postgres:
|
||||
extends:
|
||||
file: apps/backend/postgres.yml
|
||||
service: gs_postgres
|
||||
|
||||
gs_redis:
|
||||
image: "redis:alpine"
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
gs_redis_commander:
|
||||
image: rediscommander/redis-commander:latest
|
||||
environment:
|
||||
- REDIS_HOSTS=local:gs_redis:6379
|
||||
ports:
|
||||
- "8081:8081"
|
||||
depends_on:
|
||||
- gs_redis
|
||||
|
||||
volumes:
|
||||
gs_postgres-master:
|
||||
driver: local
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue