├── .husky └── pre-commit ├── intranet ├── .env.development ├── src │ ├── core │ │ ├── providers │ │ │ ├── index.ts │ │ │ └── sign-up │ │ │ │ ├── index.ts │ │ │ │ ├── sign-up.context.tsx │ │ │ │ ├── sign-up.model.ts │ │ │ │ └── sign-up.provider.tsx │ │ ├── theme │ │ │ ├── index.ts │ │ │ └── main.theme.ts │ │ ├── constants │ │ │ ├── index.ts │ │ │ └── api-urls.constants.ts │ │ ├── router │ │ │ ├── index.ts │ │ │ ├── routes.ts │ │ │ └── app-router.component.tsx │ │ └── model │ │ │ └── index.ts │ ├── layouts │ │ ├── index.ts │ │ ├── sign-up.layout.tsx │ │ └── sign-up.layout.styles.ts │ ├── scenes │ │ ├── index.ts │ │ └── sign-up.scene.tsx │ ├── vite-env.d.ts │ ├── pods │ │ ├── sign-up │ │ │ ├── index.ts │ │ │ ├── sign-up.styles.ts │ │ │ ├── sign-up.pod.tsx │ │ │ ├── sign-up.component.tsx │ │ │ └── beer-list.mock.ts │ │ ├── temporal │ │ │ ├── index.ts │ │ │ ├── api │ │ │ │ ├── index.ts │ │ │ │ └── temporal.api.ts │ │ │ └── temporal.pod.tsx │ │ └── index.ts │ ├── index.tsx │ └── app.component.tsx ├── config │ └── test │ │ ├── config.d.ts │ │ └── config.ts ├── public │ ├── duvel.png │ ├── judas-beer.png │ ├── gulden-draak.png │ ├── cuvee-des-trolls.png │ ├── gulden-draak-smoked.png │ ├── paulaner-weissbier9.png │ ├── grimbergen-blonde-blond.png │ ├── hoppy-flower-double-ipa.png │ ├── weihenstephaner-hefeweissbier.png │ ├── barbar-blonde-rubia-strong-ale.png │ └── favicon.svg ├── tsconfig.node.json ├── vite.config.ts ├── index.html ├── tsconfig.json └── package.json ├── public-portal ├── .env.local ├── src │ ├── pods │ │ ├── temporal │ │ │ ├── index.ts │ │ │ ├── api │ │ │ │ ├── index.ts │ │ │ │ └── temporal.api.ts │ │ │ └── temporal.pod.tsx │ │ ├── design-sistem │ │ │ ├── index.ts │ │ │ ├── example-components │ │ │ │ ├── index.ts │ │ │ │ ├── example-nav.tsx │ │ │ │ ├── example-standard-ui-texts.tsx │ │ │ │ ├── example-beer-grid-layout.tsx │ │ │ │ ├── example-standard-headings.tsx │ │ │ │ ├── example-grid.tsx │ │ │ │ └── example-buttons-and-texts.tsx │ │ │ ├── design-sistem.pod.tsx │ │ │ └── styles.module.scss │ │ └── index.ts │ ├── core │ │ └── constants │ │ │ ├── index.ts │ │ │ ├── env.constants.ts │ │ │ └── api-urls.constants.ts │ ├── app │ │ ├── page.tsx │ │ ├── ds │ │ │ └── page.tsx │ │ └── layout.tsx │ └── styles │ │ └── default-global-theme │ │ ├── default-global.scss │ │ ├── resets │ │ ├── _reset.scss │ │ └── _normalize-reset.scss │ │ ├── base │ │ └── _base.scss │ │ ├── buttons-links │ │ └── _buttons-links.scss │ │ ├── mixins │ │ └── _mixins.scss │ │ ├── texts │ │ └── _texts.scss │ │ ├── vars │ │ └── _style-variables.scss │ │ └── layout │ │ └── _layout.scss ├── config │ └── test │ │ ├── config.d.ts │ │ └── config.ts ├── public │ ├── home-hero-img.webp │ ├── beer-default-img.png │ ├── favicon.svg │ └── iso_logo.svg ├── next-env.d.ts ├── next.config.js ├── tsconfig.json └── package.json ├── backend ├── config │ └── test │ │ ├── config.d.ts │ │ └── config.ts ├── src │ ├── core │ │ ├── constants │ │ │ ├── index.ts │ │ │ └── env.constants.ts │ │ ├── load-env.ts │ │ └── servers │ │ │ ├── index.ts │ │ │ ├── rest-api.server.ts │ │ │ └── db.server.ts │ ├── pods │ │ └── temporal │ │ │ ├── index.ts │ │ │ └── temporal.rest-api.ts │ ├── dals │ │ ├── temporal │ │ │ ├── index.ts │ │ │ ├── temporal.model.ts │ │ │ ├── repositories │ │ │ │ ├── temporal.contract.ts │ │ │ │ ├── index.ts │ │ │ │ ├── temporal.mock-repository.ts │ │ │ │ └── temporal.repository.ts │ │ │ └── temporal.context.ts │ │ └── mock-data.ts │ ├── console-runners │ │ ├── questions.ts │ │ ├── create-temporal-data │ │ │ └── index.ts │ │ └── index.ts │ └── index.ts ├── .env.example ├── create-dev-env.js ├── tsconfig.json ├── vite.config.ts ├── docker-compose.yml └── package.json ├── .prettierignore ├── .dockerignore ├── .editorconfig ├── .prettierrc ├── README.md ├── turbo.json ├── .gitignore ├── LICENSE ├── package.json └── run-scripts ├── project-commands.ts └── index.ts /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /intranet/.env.development: -------------------------------------------------------------------------------- 1 | PUBLIC_API_URL=http://localhost:3000 2 | -------------------------------------------------------------------------------- /intranet/src/core/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sign-up'; 2 | -------------------------------------------------------------------------------- /intranet/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './main.theme'; 2 | -------------------------------------------------------------------------------- /intranet/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sign-up.layout'; 2 | -------------------------------------------------------------------------------- /intranet/src/scenes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sign-up.scene'; 2 | -------------------------------------------------------------------------------- /intranet/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public-portal/.env.local: -------------------------------------------------------------------------------- 1 | BASE_API_URL=http://localhost:3000/api 2 | -------------------------------------------------------------------------------- /intranet/src/pods/sign-up/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sign-up.pod'; 2 | -------------------------------------------------------------------------------- /intranet/src/pods/temporal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal.pod'; 2 | -------------------------------------------------------------------------------- /backend/config/test/config.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /backend/src/core/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './env.constants.js'; 2 | -------------------------------------------------------------------------------- /backend/src/pods/temporal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal.rest-api.js'; 2 | -------------------------------------------------------------------------------- /intranet/config/test/config.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /intranet/src/core/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api-urls.constants'; 2 | -------------------------------------------------------------------------------- /intranet/src/pods/temporal/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal.api'; 2 | -------------------------------------------------------------------------------- /public-portal/src/pods/temporal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal.pod'; 2 | -------------------------------------------------------------------------------- /backend/src/core/load-env.ts: -------------------------------------------------------------------------------- 1 | import { config } from 'dotenv'; 2 | config(); 3 | -------------------------------------------------------------------------------- /public-portal/config/test/config.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public-portal/src/pods/temporal/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal.api'; 2 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/index.ts: -------------------------------------------------------------------------------- 1 | export * from './design-sistem.pod'; 2 | -------------------------------------------------------------------------------- /intranet/src/pods/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal'; 2 | export * from './sign-up'; 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .angular 3 | .turbo 4 | dist 5 | *.ejs 6 | .cache 7 | public 8 | -------------------------------------------------------------------------------- /public-portal/src/pods/index.ts: -------------------------------------------------------------------------------- 1 | export * from './temporal'; 2 | export * from './design-sistem'; 3 | -------------------------------------------------------------------------------- /backend/src/core/servers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './db.server.js'; 2 | export * from './rest-api.server.js'; 3 | -------------------------------------------------------------------------------- /intranet/public/duvel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/duvel.png -------------------------------------------------------------------------------- /intranet/src/core/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-router.component'; 2 | export { routes } from './routes'; 3 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './repositories/index.js'; 2 | export * from './temporal.model.js'; 3 | -------------------------------------------------------------------------------- /intranet/public/judas-beer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/judas-beer.png -------------------------------------------------------------------------------- /public-portal/src/core/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api-urls.constants'; 2 | export * from './env.constants'; 3 | -------------------------------------------------------------------------------- /intranet/public/gulden-draak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/gulden-draak.png -------------------------------------------------------------------------------- /public-portal/src/core/constants/env.constants.ts: -------------------------------------------------------------------------------- 1 | export const ENV = { 2 | BASE_API_URL: process.env.BASE_API_URL, 3 | }; 4 | -------------------------------------------------------------------------------- /intranet/public/cuvee-des-trolls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/cuvee-des-trolls.png -------------------------------------------------------------------------------- /intranet/public/gulden-draak-smoked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/gulden-draak-smoked.png -------------------------------------------------------------------------------- /intranet/public/paulaner-weissbier9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/paulaner-weissbier9.png -------------------------------------------------------------------------------- /public-portal/public/home-hero-img.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/public-portal/public/home-hero-img.webp -------------------------------------------------------------------------------- /public-portal/public/beer-default-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/public-portal/public/beer-default-img.png -------------------------------------------------------------------------------- /intranet/public/grimbergen-blonde-blond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/grimbergen-blonde-blond.png -------------------------------------------------------------------------------- /intranet/public/hoppy-flower-double-ipa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/hoppy-flower-double-ipa.png -------------------------------------------------------------------------------- /intranet/public/weihenstephaner-hefeweissbier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/weihenstephaner-hefeweissbier.png -------------------------------------------------------------------------------- /intranet/public/barbar-blonde-rubia-strong-ale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/beer-geek-menu/main/intranet/public/barbar-blonde-rubia-strong-ale.png -------------------------------------------------------------------------------- /intranet/src/core/constants/api-urls.constants.ts: -------------------------------------------------------------------------------- 1 | const BASE_URL = '/api'; 2 | 3 | export const API_URLS = { 4 | TEMPORALS: `${BASE_URL}/temporals`, 5 | }; 6 | -------------------------------------------------------------------------------- /intranet/src/core/providers/sign-up/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sign-up.context'; 2 | export * from './sign-up.provider'; 3 | export * from './sign-up.model'; 4 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/temporal.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | export interface Temporal { 4 | _id: ObjectId; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | backups 3 | dist 4 | mongo-data 5 | node_modules 6 | .env 7 | .env.example 8 | create-dev-env.js 9 | docker-compose.yml 10 | README.md 11 | -------------------------------------------------------------------------------- /intranet/src/core/model/index.ts: -------------------------------------------------------------------------------- 1 | export interface Beer { 2 | id: string; 3 | name: string; 4 | alcohol: number; 5 | volume: number; 6 | photoUrl: string; 7 | } 8 | -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | INTERNAL_PORT=3000 3 | MOCK_API=true 4 | MONGODB_URI=mongodb://localhost:27017/beer-geek-menu 5 | FRONT_STATIC_FILES=../dist/public 6 | -------------------------------------------------------------------------------- /public-portal/src/core/constants/api-urls.constants.ts: -------------------------------------------------------------------------------- 1 | import { ENV } from './env.constants'; 2 | 3 | export const API_URLS = { 4 | TEMPORALS: `${ENV.BASE_API_URL}/temporals`, 5 | }; 6 | -------------------------------------------------------------------------------- /backend/config/test/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | restoreMocks: true, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/repositories/temporal.contract.ts: -------------------------------------------------------------------------------- 1 | import { Temporal } from '../temporal.model.js'; 2 | 3 | export interface TemporalRepository { 4 | getList(): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /intranet/src/pods/temporal/api/temporal.api.ts: -------------------------------------------------------------------------------- 1 | import { API_URLS } from '#core/constants'; 2 | 3 | export const getTemporalList = async (): Promise => fetch(API_URLS.TEMPORALS).then(response => response.json()); 4 | -------------------------------------------------------------------------------- /public-portal/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { TemporalPod } from '#pods'; 4 | 5 | const RootPage = () => { 6 | return ; 7 | }; 8 | 9 | export default RootPage; 10 | -------------------------------------------------------------------------------- /intranet/src/core/providers/sign-up/sign-up.context.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SignUpContextModel } from './sign-up.model'; 3 | 4 | export const SignUpContext = React.createContext(null); 5 | -------------------------------------------------------------------------------- /intranet/config/test/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | restoreMocks: true, 7 | environment: 'jsdom', 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /public-portal/config/test/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | restoreMocks: true, 7 | environment: 'jsdom', 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /public-portal/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /backend/src/core/servers/rest-api.server.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | export const createRestApiServer = () => { 4 | const restApiServer = express(); 5 | restApiServer.use(express.json()); 6 | 7 | return restApiServer; 8 | }; 9 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/temporal.context.ts: -------------------------------------------------------------------------------- 1 | import { dbServer } from '#core/servers/index.js'; 2 | import { Temporal } from './temporal.model.js'; 3 | 4 | export const getTemportalContext = () => dbServer.db?.collection('temporals'); 5 | -------------------------------------------------------------------------------- /intranet/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | import { App } from './app.component'; 3 | 4 | const rootElement = document.getElementById('root')!; 5 | const root = createRoot(rootElement); 6 | 7 | root.render(); 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "bracketSpacing": true, 8 | "trailingComma": "es5", 9 | "arrowParens": "avoid", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /public-portal/src/pods/temporal/api/temporal.api.ts: -------------------------------------------------------------------------------- 1 | import { API_URLS } from '#core/constants'; 2 | 3 | export const getTemporalList = async (options?: RequestInit): Promise => 4 | fetch(API_URLS.TEMPORALS, options).then(response => response.json()); 5 | -------------------------------------------------------------------------------- /public-portal/src/app/ds/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { DesignSistemPod } from '#pods/design-sistem/design-sistem.pod'; 4 | 5 | const DesignSistemPage = () => { 6 | return ; 7 | }; 8 | 9 | export default DesignSistemPage; 10 | -------------------------------------------------------------------------------- /intranet/src/scenes/sign-up.scene.tsx: -------------------------------------------------------------------------------- 1 | import { SignUpLayout } from '#layouts'; 2 | import { SignUpPod } from '#pods'; 3 | 4 | export const SignUpScene: React.FC = () => { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /intranet/src/core/router/routes.ts: -------------------------------------------------------------------------------- 1 | interface PathRoutes { 2 | root: string; 3 | } 4 | 5 | export const pathRoutes: PathRoutes = { 6 | root: '/', 7 | }; 8 | 9 | interface Routes extends PathRoutes {} 10 | 11 | export const routes: Routes = { 12 | ...pathRoutes, 13 | }; 14 | -------------------------------------------------------------------------------- /backend/src/console-runners/questions.ts: -------------------------------------------------------------------------------- 1 | import { PromptObject } from 'prompts'; 2 | 3 | export const mongoDBQuestions: PromptObject[] = [ 4 | { 5 | name: 'connectionString', 6 | type: 'text', 7 | message: 'Connection string (Press enter to use default): ', 8 | }, 9 | ]; 10 | -------------------------------------------------------------------------------- /backend/create-dev-env.js: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'node:fs'; 2 | import { copyFile } from 'node:fs/promises'; 3 | 4 | const ENV_EXAMPLE = './.env.example'; 5 | const ENV = './.env'; 6 | const exist = existsSync(ENV); 7 | 8 | if (!exist) { 9 | await copyFile(ENV_EXAMPLE, ENV); 10 | } 11 | -------------------------------------------------------------------------------- /backend/src/pods/temporal/temporal.rest-api.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { temporalRepository } from '#dals/temporal/index.js'; 3 | 4 | export const temporalAPI = Router(); 5 | 6 | temporalAPI.get('/', async (req, res) => { 7 | res.send(await temporalRepository.getList()); 8 | }); 9 | -------------------------------------------------------------------------------- /intranet/src/pods/sign-up/sign-up.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const buttons = css` 4 | width: 100%; 5 | display: flex; 6 | justify-content: space-between; 7 | `; 8 | 9 | export const steps = css` 10 | min-height: 400px; 11 | width: 100%; 12 | `; 13 | -------------------------------------------------------------------------------- /public-portal/next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** 4 | * @type {import('next').NextConfig} 5 | */ 6 | const nextConfig = { 7 | sassOptions: { 8 | // includePaths: [path.join(__dirname, 'css')], 9 | includePaths: ['css'], 10 | }, 11 | }; 12 | 13 | export default nextConfig; 14 | -------------------------------------------------------------------------------- /intranet/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/default-global.scss: -------------------------------------------------------------------------------- 1 | @use './resets/reset' as *; 2 | @use './vars/style-variables' as *; 3 | @use './base/base' as *; 4 | @use './layout/layout' as *; 5 | 6 | @use './mixins/mixins' as *; 7 | @use './texts/texts' as *; 8 | @use './buttons-links/buttons-links' as *; 9 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/repositories/index.ts: -------------------------------------------------------------------------------- 1 | import { ENV } from '#core/constants/index.js'; 2 | import { mockRepository } from './temporal.mock-repository.js'; 3 | import { dbRepository } from './temporal.repository.js'; 4 | 5 | export const temporalRepository = ENV.IS_MOCK_API ? mockRepository : dbRepository; 6 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/repositories/temporal.mock-repository.ts: -------------------------------------------------------------------------------- 1 | import { TemporalRepository } from './temporal.contract.js'; 2 | import { mockDB } from '#dals/mock-data.js'; 3 | 4 | export const mockRepository: TemporalRepository = { 5 | getList: async () => { 6 | return mockDB.temporals; 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /backend/src/core/constants/env.constants.ts: -------------------------------------------------------------------------------- 1 | export const ENV = { 2 | IS_PRODUCTION: process.env.NODE_ENV === 'production', 3 | PORT: process.env.INTERNAL_PORT, 4 | IS_MOCK_API: process.env.MOCK_API === 'true', 5 | MONGODB_URI: process.env.MONGODB_URI, 6 | FRONT_STATIC_FILES: process.env.FRONT_STATIC_FILES, 7 | }; 8 | -------------------------------------------------------------------------------- /intranet/src/layouts/sign-up.layout.tsx: -------------------------------------------------------------------------------- 1 | import * as classes from './sign-up.layout.styles'; 2 | 3 | interface Props extends React.PropsWithChildren {} 4 | 5 | export const SignUpLayout: React.FC = props => { 6 | const { children } = props; 7 | return
{children}
; 8 | }; 9 | -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "skipLibCheck": true, 7 | "isolatedModules": true, 8 | "esModuleInterop": true 9 | }, 10 | "include": ["src/**/*", "config/test/config.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './example-beer-grid-layout'; 2 | export * from './example-buttons-and-texts'; 3 | export * from './example-grid'; 4 | export * from './example-nav'; 5 | export * from './example-standard-headings'; 6 | export * from './example-standard-ui-texts'; 7 | -------------------------------------------------------------------------------- /intranet/src/pods/sign-up/sign-up.pod.tsx: -------------------------------------------------------------------------------- 1 | import { SignUpComponent } from './sign-up.component'; 2 | import { useSignUpContext } from '#core/providers/sign-up'; 3 | export const SignUpPod: React.FC = () => { 4 | const { signUpData } = useSignUpContext(); 5 | 6 | console.log(signUpData); 7 | return ; 8 | }; 9 | -------------------------------------------------------------------------------- /backend/src/console-runners/create-temporal-data/index.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | import { getTemportalContext } from '#dals/temporal/temporal.context.js'; 3 | 4 | export const run = async () => { 5 | await getTemportalContext().insertOne({ 6 | _id: new ObjectId(), 7 | name: 'Temporal from Mongo', 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /backend/src/dals/temporal/repositories/temporal.repository.ts: -------------------------------------------------------------------------------- 1 | import { TemporalRepository } from './temporal.contract.js'; 2 | import { getTemportalContext } from '../temporal.context.js'; 3 | 4 | export const dbRepository: TemporalRepository = { 5 | getList: async () => { 6 | return await getTemportalContext().find().toArray(); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /backend/src/dals/mock-data.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | import { Temporal } from './temporal/index.js'; 3 | 4 | export interface MockDB { 5 | temporals: Temporal[]; 6 | } 7 | 8 | export const mockDB: MockDB = { 9 | temporals: [ 10 | { _id: new ObjectId(), name: 'Temporal 1' }, 11 | { _id: new ObjectId(), name: 'Temporal 2' }, 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /backend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { externalizeDeps } from 'vite-plugin-externalize-deps'; 3 | 4 | export default defineConfig({ 5 | plugins: [externalizeDeps()], 6 | build: { 7 | target: 'node20', 8 | lib: { 9 | entry: 'src/index.ts', 10 | formats: ['es'], 11 | fileName: 'index', 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/resets/_reset.scss: -------------------------------------------------------------------------------- 1 | @use '_normalize-reset' as *; 2 | 3 | *, 4 | *:before, 5 | *:after { 6 | box-sizing: border-box; 7 | min-width: 0; 8 | } 9 | 10 | body { 11 | min-height: 100dvh; 12 | margin: 0; 13 | } 14 | 15 | h1, 16 | h2, 17 | h3, 18 | h4, 19 | h5, 20 | h6 { 21 | text-wrap: balance; 22 | } 23 | 24 | p { 25 | text-wrap: pretty; 26 | } 27 | -------------------------------------------------------------------------------- /intranet/src/layouts/sign-up.layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | import { mainTheme } from '#core/theme'; 4 | 5 | export const container = css` 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | flex-direction: column; 10 | margin: 0 auto; 11 | min-height: 100vh; 12 | width: 90%; 13 | max-width: 380px; 14 | gap: ${mainTheme.spacing(2)}; 15 | `; 16 | -------------------------------------------------------------------------------- /intranet/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | 4 | export default defineConfig(({ mode }) => { 5 | const env = loadEnv(mode, process.cwd(), ''); 6 | return { 7 | envPrefix: 'PUBLIC_', 8 | plugins: [react()], 9 | server: { 10 | proxy: { 11 | '/api': env.PUBLIC_API_URL, 12 | }, 13 | }, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /intranet/src/core/router/app-router.component.tsx: -------------------------------------------------------------------------------- 1 | import { HashRouter as Router, Route, Routes } from 'react-router-dom'; 2 | 3 | import { SignUpScene } from '#scenes'; 4 | 5 | import { pathRoutes } from './routes'; 6 | 7 | export const AppRouter = () => { 8 | return ( 9 | 10 | 11 | } /> 12 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /intranet/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Intranet: Beer Geek Menu 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/example-nav.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from '../styles.module.scss'; 3 | 4 | export function ExampleNav() { 5 | return ( 6 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /public-portal/src/pods/temporal/temporal.pod.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import * as api from './api'; 4 | 5 | export const TemporalPod = async () => { 6 | const temporals = await api.getTemporalList(); 7 | 8 | return ( 9 | <> 10 |

Public Portal Temporal

11 |
    12 | {temporals.map(temporal => ( 13 |
  • {temporal.name}
  • 14 | ))} 15 |
16 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /backend/src/core/servers/db.server.ts: -------------------------------------------------------------------------------- 1 | import { MongoClient, Db } from 'mongodb'; 2 | 3 | let client: MongoClient; 4 | 5 | const connect = async (connectionURI: string) => { 6 | client = new MongoClient(connectionURI); 7 | await client.connect(); 8 | dbServer.db = client.db(); 9 | }; 10 | 11 | const disconnect = async () => { 12 | await client.close(); 13 | }; 14 | 15 | export let dbServer = { 16 | connect, 17 | disconnect, 18 | db: undefined as Db, 19 | }; 20 | -------------------------------------------------------------------------------- /backend/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | beer-geek-menu: 4 | container_name: beer-geek-menu 5 | image: mongo:7 6 | ports: 7 | - '27017:27017' 8 | volumes: 9 | - type: bind 10 | source: ./mongo-data 11 | target: /data/db 12 | - type: bind 13 | source: ./backups 14 | target: /backups 15 | networks: 16 | - beer-geek-menu-network 17 | volumes: 18 | mongo-data: 19 | backups: 20 | networks: 21 | beer-geek-menu-network: 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beer-geek-menu 2 | 3 | ## Run commands 4 | 5 | Start 6 | 7 | ```bash 8 | npm start 9 | ``` 10 | 11 | > It opens a terminal menu to select the app or apps to run. 12 | 13 | Console-runners (backend utilities) to create initial seed data: 14 | 15 | ```bash 16 | npm run console-runners 17 | ``` 18 | 19 | > It opens a terminal menu to select the project to run console-runners. 20 | 21 | Watch mode tests: 22 | 23 | ```bash 24 | npm run test:watch 25 | ``` 26 | 27 | > It opens a terminal menu to select the app to run tests. 28 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "pipeline": { 4 | "start": { 5 | "cache": false, 6 | "persistent": true 7 | }, 8 | "type-check:watch": { 9 | "cache": false, 10 | "persistent": true 11 | }, 12 | "clean": {}, 13 | "build": { 14 | "outputs": ["dist/**/*"], 15 | "dependsOn": ["^build", "clean", "type-check"] 16 | }, 17 | "type-check": { 18 | "dependsOn": ["^build"] 19 | }, 20 | "test": { 21 | "dependsOn": ["^build"] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | dist 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | .pnpm-debug.log* 26 | 27 | # local env files 28 | .env 29 | 30 | # turbo 31 | .turbo 32 | 33 | stats.html 34 | 35 | mongo-data 36 | backups 37 | .cache 38 | 39 | vite.config.ts.timestamp* 40 | tsconfig.tsbuildinfo 41 | -------------------------------------------------------------------------------- /public-portal/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import '@fontsource-variable/oswald'; 4 | import '#styles/default-global-theme/default-global.scss'; 5 | import { Metadata } from 'next'; 6 | 7 | interface Props { 8 | children: React.ReactNode; 9 | } 10 | 11 | export const metadata: Metadata = { 12 | title: 'Public Portal: Design Sistem', 13 | icons: ['/favicon.svg'], 14 | }; 15 | 16 | const RootLayout = (props: Props) => { 17 | const { children } = props; 18 | return ( 19 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | export default RootLayout; 26 | -------------------------------------------------------------------------------- /intranet/src/pods/temporal/temporal.pod.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as api from './api'; 3 | 4 | export const TemporalPod = () => { 5 | const [temporals, setTemporals] = React.useState<{ id: string; name: string }[]>([]); 6 | 7 | const onLoad = () => { 8 | api.getTemporalList().then(setTemporals); 9 | }; 10 | 11 | React.useEffect(() => { 12 | onLoad(); 13 | }, []); 14 | 15 | return ( 16 | <> 17 |

Intranet Temporal

18 |
    19 | {temporals.map(temporal => ( 20 |
  • {temporal.name}
  • 21 | ))} 22 |
23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /intranet/src/pods/sign-up/sign-up.component.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Typography } from '@mui/material'; 2 | 3 | import * as classes from './sign-up.styles'; 4 | 5 | export const SignUpComponent: React.FC = () => { 6 | return ( 7 | <> 8 | 9 | Crea tu cuenta 10 | 11 |
{/* Components for diferents estates of sign-in */}
12 |
13 | 16 | 17 |
18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/example-standard-ui-texts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from '../styles.module.scss'; 4 | 5 | export function ExampleStandardUiTexts() { 6 | return ( 7 |
8 |

Standard UI texts

9 |

.txt-title .txt-title--bold

10 |

.txt-large

11 |

.txt-common (default size)

12 |

.txt-detail

13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /intranet/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src/**/*", "config/test/config.d.ts"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /public-portal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": false, 7 | "noEmit": true, 8 | "incremental": true, 9 | "module": "esnext", 10 | "esModuleInterop": true, 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "baseUrl": ".", 21 | "paths": { 22 | "#*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "next.config.cjs", "next.config.js"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /intranet/src/app.component.tsx: -------------------------------------------------------------------------------- 1 | import '@fontsource-variable/oswald'; 2 | import '@fontsource/roboto/100.css'; 3 | import '@fontsource/roboto/300.css'; 4 | import '@fontsource/roboto/400.css'; 5 | import '@fontsource/roboto/500.css'; 6 | import '@fontsource/roboto/700.css'; 7 | import '@fontsource/roboto/900.css'; 8 | import { CssBaseline, ThemeProvider } from '@mui/material'; 9 | 10 | import { AppRouter } from '#core/router'; 11 | import { mainTheme } from '#core/theme'; 12 | import { SignUpProvider } from '#core/providers/'; 13 | 14 | export const App = () => { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /backend/src/index.ts: -------------------------------------------------------------------------------- 1 | import '#core/load-env.js'; 2 | import path from 'node:path'; 3 | import express from 'express'; 4 | import { createRestApiServer, dbServer } from '#core/servers/index.js'; 5 | import { ENV } from '#core/constants/index.js'; 6 | import { temporalAPI } from '#pods/temporal/index.js'; 7 | 8 | const app = createRestApiServer(); 9 | const staticFilesPath = path.resolve(import.meta.dirname, ENV.FRONT_STATIC_FILES); 10 | app.use('/', express.static(staticFilesPath)); 11 | 12 | app.use('/api/temporals', temporalAPI); 13 | 14 | app.listen(ENV.PORT, async () => { 15 | if (ENV.IS_MOCK_API) { 16 | console.log('Mock mode'); 17 | } else { 18 | console.log('MongoDB mode'); 19 | await dbServer.connect(ENV.MONGODB_URI); 20 | } 21 | console.log(`Server is running on port ${ENV.PORT}`); 22 | }); 23 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/base/_base.scss: -------------------------------------------------------------------------------- 1 | @import '~@fontsource-variable/oswald/index.css'; 2 | @import '~@fontsource/roboto/100.css'; 3 | @import '~@fontsource/roboto/300.css'; 4 | @import '~@fontsource/roboto/400.css'; 5 | @import '~@fontsource/roboto/500.css'; 6 | @import '~@fontsource/roboto/700.css'; 7 | @import '~@fontsource/roboto/900.css'; 8 | 9 | html { 10 | font-size: var(--fs-master); 11 | } 12 | 13 | body { 14 | font-family: var(--ff-main); 15 | font-size: 1.6rem; 16 | font-style: normal; 17 | font-weight: var(--fw-normal); 18 | font-style: normal; 19 | background-color: var(--color-bg-primary); 20 | color: var(--color-txt-primary); 21 | 22 | margin: 0 auto; 23 | min-height: 100vh; 24 | min-height: 100dvh; 25 | max-width: 100dvw; 26 | } 27 | 28 | img.responsive { 29 | max-width: 100%; 30 | image-rendering: smooth; 31 | height: auto; 32 | } 33 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/design-sistem.pod.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import { 3 | ExampleBeerGridLayout, 4 | ExampleButtonsAndTexts, 5 | ExampleGrid, 6 | ExampleNav, 7 | ExampleStandardHeadings, 8 | ExampleStandardUiTexts, 9 | } from './example-components'; 10 | import styles from './styles.module.scss'; 11 | 12 | export const DesignSistemPod = () => { 13 | return ( 14 | <> 15 | 16 |
17 |

Examples with global styles

18 | 19 | 20 | 21 | 22 | 23 |

Examples custom styles using mixins

24 |
25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /public-portal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@beer-geek-menu/public-portal", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "start": "next dev --port 8080", 7 | "clean": "rimraf dist", 8 | "build": "next build", 9 | "type-check": "tsc --noEmit --preserveWatchOutput", 10 | "type-check:watch": "npm run type-check -- --watch", 11 | "test": "vitest run -c ./config/test/config.ts", 12 | "test:watch": "vitest -c ./config/test/config.ts" 13 | }, 14 | "imports": { 15 | "#*": [ 16 | "./src/*", 17 | "./src/*.ts", 18 | "./src/*.tsx", 19 | "./src/*/index.ts", 20 | "./src/*/index.tsx" 21 | ] 22 | }, 23 | "dependencies": { 24 | "@fontsource-variable/oswald": "^5.0.20", 25 | "@fontsource/roboto": "^5.0.13", 26 | "next": "^14.2.3" 27 | }, 28 | "devDependencies": { 29 | "clsx": "^2.1.1", 30 | "jsdom": "^24.0.0", 31 | "sass": "^1.77.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /intranet/src/core/providers/sign-up/sign-up.model.ts: -------------------------------------------------------------------------------- 1 | import { Beer } from '#core/model'; 2 | 3 | export interface Restaurant { 4 | name: string; 5 | address: string; 6 | city: string; 7 | phoneNumber: string; 8 | } 9 | 10 | export interface SignUpModel { 11 | userName: string; 12 | password: string; 13 | email: string; 14 | restaurant: Restaurant; 15 | beers: Beer[]; 16 | } 17 | 18 | export const createInitialSignUp = (): SignUpModel => ({ 19 | userName: '', 20 | password: '', 21 | email: '', 22 | restaurant: { 23 | name: '', 24 | address: '', 25 | city: '', 26 | phoneNumber: '', 27 | }, 28 | beers: [], 29 | }); 30 | 31 | export interface SignUpContextModel { 32 | signUpData: SignUpModel; 33 | setUserName: (userName: string) => void; 34 | setPassword: (password: string) => void; 35 | setEmail: (email: string) => void; 36 | setRestaurants: (restaurant: Restaurant) => void; 37 | setBeers: (beers: Beer[]) => void; 38 | } 39 | -------------------------------------------------------------------------------- /intranet/src/core/theme/main.theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@mui/material/styles'; 2 | 3 | export const mainTheme = createTheme({ 4 | palette: { 5 | mode: 'light', 6 | primary: { 7 | main: '#3a2e23', 8 | contrastText: '#ffcf20', 9 | }, 10 | secondary: { 11 | main: '#ffcf20', 12 | contrastText: '#3a2e23', 13 | }, 14 | success: { 15 | main: '#22c14b', 16 | }, 17 | error: { 18 | main: '#f32400', 19 | }, 20 | background: { 21 | default: '#fffbf3', 22 | }, 23 | }, 24 | typography: { 25 | h1: { 26 | fontFamily: 'Oswald', 27 | fontWeight: 600, 28 | }, 29 | h2: { 30 | fontFamily: 'Oswald', 31 | fontWeight: 600, 32 | }, 33 | h3: { 34 | fontFamily: 'Oswald', 35 | fontWeight: 600, 36 | }, 37 | h4: { 38 | fontFamily: 'Oswald', 39 | fontWeight: 600, 40 | }, 41 | }, 42 | spacing: 8, 43 | shape: { 44 | borderRadius: 3, 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/example-beer-grid-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from '../styles.module.scss'; 3 | 4 | export function ExampleBeerGridLayout() { 5 | return ( 6 |
7 |

Beer catalog grid layout

8 |
    9 |
  • 10 |
    11 | 12 |
    13 |
    14 |

    Beer Name

    15 |

    ##€

    16 |
    17 |

    18 | Beer description. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quia eligendi praesentium 19 | et sunt omnis inventore. 20 |

    21 |
  • 22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '#styles/default-global-theme/mixins/mixins' as *; 2 | 3 | .custom { 4 | color: green; 5 | font-size: 3rem; 6 | @include lightText(); 7 | } 8 | 9 | .nav__title { 10 | display: flex; 11 | align-items: center; 12 | gap: var(--space-xs); 13 | text-transform: uppercase; 14 | padding: var(--space-md); 15 | padding-left: var(--space-lg); 16 | margin-block-start: 0; 17 | &__logo { 18 | max-height: 40em; 19 | } 20 | } 21 | 22 | .container { 23 | max-width: var(--bkp-tablet-lg); 24 | width: 100dvw; 25 | margin: 0 auto; 26 | padding-top: var(--space-xl); 27 | padding-bottom: var(--space-xl); 28 | } 29 | 30 | .example_block { 31 | padding: var(--space-lg) var(--space-md); 32 | } 33 | 34 | .grid_example_block { 35 | padding: var(--space-lg) 0; 36 | } 37 | 38 | .flex_container { 39 | display: flex; 40 | flex-wrap: wrap; 41 | gap: var(--space-lg); 42 | 43 | & > div { 44 | flex-grow: 1; 45 | } 46 | } 47 | 48 | .link_btn_container { 49 | margin-bottom: var(--space-md); 50 | } 51 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@beer-geek-menu/backend", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "start:db": "docker compose up -d", 7 | "prestart": "node ./create-dev-env.js", 8 | "start": "nodemon --exec tsx src/index.ts", 9 | "preconsole-runners": "npm run prestart", 10 | "console-runners": "nodemon --no-stdin --exec tsx src/console-runners/index.ts", 11 | "clean": "rimraf dist", 12 | "build": "vite build", 13 | "type-check": "tsc --noEmit --preserveWatchOutput", 14 | "type-check:watch": "npm run type-check -- --watch", 15 | "test": "vitest run -c ./config/test/config.ts", 16 | "test:watch": "vitest -c ./config/test/config.ts" 17 | }, 18 | "imports": { 19 | "#common/*": "./src/common/*", 20 | "#core/*": "./src/core/*", 21 | "#dals/*": "./src/dals/*", 22 | "#pods/*": "./src/pods/*" 23 | }, 24 | "dependencies": { 25 | "dotenv": "^16.4.5", 26 | "express": "^4.19.2", 27 | "mongodb": "^6.5.0" 28 | }, 29 | "devDependencies": { 30 | "@types/express": "^4.17.21" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /intranet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@beer-geek-menu/intranet", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "start": "vite --port 8081", 7 | "clean": "rimraf dist", 8 | "build": "vite build", 9 | "type-check": "tsc --noEmit --preserveWatchOutput", 10 | "type-check:watch": "npm run type-check -- --watch", 11 | "test": "vitest run -c ./config/test/config.ts", 12 | "test:watch": "vitest -c ./config/test/config.ts" 13 | }, 14 | "imports": { 15 | "#*": [ 16 | "./src/*", 17 | "./src/*.ts", 18 | "./src/*.tsx", 19 | "./src/*/index.ts", 20 | "./src/*/index.tsx" 21 | ] 22 | }, 23 | "dependencies": { 24 | "@emotion/css": "^11.11.2", 25 | "@emotion/react": "^11.11.4", 26 | "@emotion/styled": "^11.11.5", 27 | "@mui/material": "^5.15.18", 28 | "react": "^18.3.1", 29 | "react-dom": "^18.3.1", 30 | "react-router-dom": "^6.23.0" 31 | }, 32 | "devDependencies": { 33 | "@types/react": "^18.3.1", 34 | "@types/react-dom": "^18.3.0", 35 | "@vitejs/plugin-react": "^4.2.1", 36 | "jsdom": "^24.0.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Lemoncode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/example-standard-headings.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from '../styles.module.scss'; 4 | 5 | export function ExampleStandardHeadings() { 6 | return ( 7 |
8 |

Standard Headings:

9 |

Hero text

10 |

Global/standard H1

11 |

Global/standard H2

12 |

Global/standard H3

13 |

Global/standard H4

14 |

.txt--primary (default)

15 |

.txt--secondary

16 |

17 | .txt--accent

(use only over dark colors)

18 |

19 |

20 | .txt--contrast

(use only over dark colors)

21 |

22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beer-geek-menu", 3 | "private": true, 4 | "type": "module", 5 | "workspaces": [ 6 | "public-portal", 7 | "intranet", 8 | "backend" 9 | ], 10 | "scripts": { 11 | "prescripts": "npm run start:db -w @beer-geek-menu/backend", 12 | "scripts": "tsx ./run-scripts", 13 | "start": "npm run scripts -- start", 14 | "console-runners": "npm run scripts -- console-runners", 15 | "test:watch": "npm run scripts -- test:watch", 16 | "build": "turbo build", 17 | "test": "turbo test", 18 | "prepare": "husky || echo 'noop Huksy not installed'" 19 | }, 20 | "lint-staged": { 21 | "**/*": "prettier --write --ignore-unknown ." 22 | }, 23 | "devDependencies": { 24 | "@types/prompts": "^2.4.9", 25 | "husky": "^9.0.11", 26 | "nodemon": "^3.1.0", 27 | "prettier": "^3.2.5", 28 | "prompts": "^2.4.2", 29 | "rimraf": "^5.0.5", 30 | "tsx": "^4.7.3", 31 | "turbo": "^1.13.3", 32 | "typescript": "^5.4.5", 33 | "vite": "^5.2.10", 34 | "vite-plugin-externalize-deps": "^0.8.0", 35 | "vitest": "^1.5.2" 36 | }, 37 | "dependencies": { 38 | "@fontsource-variable/oswald": "^5.0.20", 39 | "@fontsource/roboto": "^5.0.13" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /run-scripts/project-commands.ts: -------------------------------------------------------------------------------- 1 | const commandType = process.argv[2]; 2 | 3 | interface Project { 4 | name: string; 5 | filterCommand: string; 6 | } 7 | 8 | const projectMap: Record = { 9 | start: [ 10 | { 11 | name: '@beer-geek-menu/public-portal', 12 | filterCommand: '--filter=@beer-geek-menu/public-portal...', 13 | }, 14 | { 15 | name: '@beer-geek-menu/intranet', 16 | filterCommand: '--filter=@beer-geek-menu/intranet...', 17 | }, 18 | { 19 | name: '@beer-geek-menu/backend', 20 | filterCommand: '--filter=@beer-geek-menu/backend...', 21 | }, 22 | ], 23 | 'console-runners': [ 24 | { 25 | name: '@beer-geek-menu/backend', 26 | filterCommand: '-w @beer-geek-menu/backend', 27 | }, 28 | ], 29 | 'test:watch': [ 30 | { 31 | name: '@beer-geek-menu/public-portal', 32 | filterCommand: '-w @beer-geek-menu/public-portal', 33 | }, 34 | { 35 | name: '@beer-geek-menu/intranet', 36 | filterCommand: '-w @beer-geek-menu/intranet', 37 | }, 38 | { 39 | name: '@beer-geek-menu/backend', 40 | filterCommand: '-w @beer-geek-menu/backend', 41 | }, 42 | ], 43 | }; 44 | 45 | export const projectList = projectMap[commandType]; 46 | -------------------------------------------------------------------------------- /backend/src/console-runners/index.ts: -------------------------------------------------------------------------------- 1 | import '#core/load-env.js'; 2 | import prompts from 'prompts'; 3 | import fs from 'node:fs/promises'; 4 | import { dbServer } from '#core/servers/index.js'; 5 | import { ENV } from '#core/constants/index.js'; 6 | import { mongoDBQuestions } from './questions.js'; 7 | 8 | const consoleRunners = await fs 9 | .readdir(import.meta.dirname, { withFileTypes: true }) 10 | .then(files => files.filter(file => file.isDirectory()).map(file => file.name)); 11 | 12 | let exit = false; 13 | const mongoDbFields = await prompts(mongoDBQuestions); 14 | const connectionString = Boolean(mongoDbFields.connectionString) ? mongoDbFields.connectionString : ENV.MONGODB_URI; 15 | await dbServer.connect(connectionString); 16 | while (!exit) { 17 | const { consoleRunner } = await prompts([ 18 | { 19 | name: 'consoleRunner', 20 | type: 'select', 21 | message: 'Which test-runner do you want to run?', 22 | choices: [...consoleRunners, 'exit'].map(runner => ({ title: runner, value: runner })), 23 | }, 24 | ]); 25 | 26 | if (consoleRunner !== 'exit') { 27 | const { run } = await import(`./${consoleRunner}/index.js`); 28 | await run(); 29 | } else { 30 | exit = true; 31 | await dbServer.disconnect(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /run-scripts/index.ts: -------------------------------------------------------------------------------- 1 | import childProcess from 'node:child_process'; 2 | import prompts from 'prompts'; 3 | import { projectList } from './project-commands'; 4 | 5 | const commandType = process.argv[2]; 6 | 7 | const promptType: Record = { 8 | start: 'multiselect', 9 | 'console-runners': 'select', 10 | 'test:watch': 'select', 11 | }; 12 | 13 | const { selectedFilterCommands } = 14 | projectList.length > 1 15 | ? await prompts([ 16 | { 17 | type: promptType[commandType], 18 | name: 'selectedFilterCommands', 19 | message: 'Select projects to run', 20 | choices: projectList.map(project => ({ 21 | title: project.name, 22 | value: project.filterCommand, 23 | })), 24 | }, 25 | ]) 26 | : { selectedFilterCommands: projectList[0].filterCommand }; 27 | 28 | const filter = Array.isArray(selectedFilterCommands) 29 | ? selectedFilterCommands.length > 0 30 | ? `${selectedFilterCommands.join(' ')}` 31 | : '' 32 | : selectedFilterCommands; 33 | 34 | const commandMap = { 35 | start: `turbo start type-check:watch ${filter}`, 36 | 'console-runners': `npm run console-runners ${filter}`, 37 | 'test:watch': `npm run test:watch ${filter}`, 38 | }; 39 | 40 | const command = `${commandMap[commandType]}`; 41 | childProcess.execSync(command, { stdio: 'inherit' }); 42 | -------------------------------------------------------------------------------- /intranet/src/pods/sign-up/beer-list.mock.ts: -------------------------------------------------------------------------------- 1 | import { Beer } from '#core/model'; 2 | 3 | export const beerListMock: Beer[] = [ 4 | { id: '1', name: 'Duvel', alcohol: 8.5, volume: 33, photoUrl: '/duvel.png' }, 5 | { 6 | id: '2', 7 | name: 'Gulden Draak Classic', 8 | alcohol: 10.5, 9 | volume: 33, 10 | photoUrl: '/gulden-draak-classic.png', 11 | }, 12 | { 13 | id: '3', 14 | name: 'Weihenstephaner Hefeweissbier', 15 | alcohol: 5.4, 16 | volume: 50, 17 | photoUrl: '/weihenstephaner-hefeweissbier.png', 18 | }, 19 | { 20 | id: '4', 21 | name: 'Gulden Draak Smoked', 22 | alcohol: 10.5, 23 | volume: 33, 24 | photoUrl: '/gulden-draak-smoked.png', 25 | }, 26 | { 27 | id: '5', 28 | name: 'Barbar Blonde Rubia Strong Ale', 29 | alcohol: 8, 30 | volume: 33, 31 | photoUrl: '/barbar-blonde-rubia-strong-ale.png', 32 | }, 33 | { id: '6', name: 'Judas', alcohol: 8.5, volume: 33, photoUrl: '/judas.png' }, 34 | { id: '7', name: 'Hoppy Flower DIPA', alcohol: 7.5, volume: 33, photoUrl: '/hoppy-flower-dipa.png' }, 35 | { id: '8', name: 'Paulaner', alcohol: 5.5, volume: 33, photoUrl: '/paulaner.png' }, 36 | { id: '9', name: 'Cuvee des Trolls', alcohol: 7, volume: 33, photoUrl: '/cuvee-des-trolls.png' }, 37 | { 38 | id: '10', 39 | name: 'Barbar Blonde Rubia Strong Ale', 40 | alcohol: 8, 41 | volume: 33, 42 | photoUrl: '/barbar-blonde-rubia-strong-ale.png', 43 | }, 44 | ]; 45 | -------------------------------------------------------------------------------- /intranet/public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /intranet/src/core/providers/sign-up/sign-up.provider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { SignUpContext } from './sign-up.context'; 3 | import { SignUpModel, createInitialSignUp, Restaurant } from './sign-up.model'; 4 | import { Beer } from '#core/model'; 5 | 6 | interface Props { 7 | children: React.ReactNode; 8 | } 9 | 10 | export const SignUpProvider: React.FC = props => { 11 | const { children } = props; 12 | const [signUpData, setSignUpData] = useState(createInitialSignUp); 13 | 14 | const setUserName = (userName: string) => { 15 | setSignUpData({ ...signUpData, userName }); 16 | }; 17 | 18 | const setPassword = (password: string) => { 19 | setSignUpData({ ...signUpData, password }); 20 | }; 21 | 22 | const setEmail = (email: string) => { 23 | setSignUpData({ ...signUpData, email }); 24 | }; 25 | 26 | const setRestaurants = (restaurant: Restaurant) => { 27 | setSignUpData({ ...signUpData, restaurant }); 28 | }; 29 | 30 | const setBeers = (beers: Beer[]) => { 31 | setSignUpData({ ...signUpData, beers }); 32 | }; 33 | 34 | return ( 35 | 36 | {children} 37 | 38 | ); 39 | }; 40 | 41 | export const useSignUpContext = () => { 42 | const context = React.useContext(SignUpContext); 43 | if (context === null) { 44 | throw 'useSignUpContext: looks like you have forgotten to add the provider on top of the app :)'; 45 | } 46 | 47 | return context; 48 | }; 49 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/buttons-links/_buttons-links.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/mixins' as *; 2 | 3 | button, 4 | .btn { 5 | @include button(); 6 | &:hover { 7 | background-color: var(--color-primary-lighter); 8 | } 9 | 10 | &.btn--small { 11 | font-size: var(--fs-sm); 12 | } 13 | 14 | &.btn--large { 15 | font-size: var(--fs-lg); 16 | } 17 | } 18 | 19 | .btn-accent { 20 | @include button(var(--color-accent), var(--color-txt-primary)); 21 | 22 | &:hover { 23 | background-color: var(--color-accent-darker); 24 | } 25 | } 26 | 27 | .btn-primary { 28 | @include button(var(--color-primary), var(--color-txt-accent)); 29 | &:hover { 30 | background-color: var(--color-primary-lighter); 31 | } 32 | } 33 | 34 | .btn-secondary { 35 | @include button(var(--color-secondary), var(--color-txt-contrast)); 36 | &:hover { 37 | background-color: var(--color-secondary-lighter); 38 | } 39 | } 40 | 41 | a, 42 | a.link--info { 43 | transition: all ease 250ms; 44 | color: var(--color-info); 45 | 46 | &:hover { 47 | color: var(--color-info-darker); 48 | } 49 | &:visited { 50 | color: var(--color-info-darker); 51 | } 52 | } 53 | 54 | a.link--accent { 55 | color: var(--color-accent); 56 | 57 | &:hover { 58 | color: var(--color-accent-darker); 59 | } 60 | &:visited { 61 | color: var(--color-accent-darker); 62 | } 63 | } 64 | 65 | a.link--secondary { 66 | color: var(--color-secondary); 67 | 68 | &:hover { 69 | color: var(--color-secondary-darker); 70 | } 71 | &:visited { 72 | color: var(--color-secondary-darker); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/example-grid.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from '../styles.module.scss'; 4 | 5 | export function ExampleGrid() { 6 | return ( 7 |
8 |

Mobile Grid:

9 |
10 |
.block-mob__item
11 |
.block-mob__item--2col
12 |
.block-mob__item--2col
13 |
.block-mob__item--3col
14 |
.block-mob__item--3col
15 |
.block-mob__item--3col
16 |
.block-mob__item--4col
17 |
.block-mob__item--4col
18 |
.block-mob__item--4col
19 |
.block-mob__item--4col
20 |
.block-mob__item--4col
21 |
.block-mob__item--2col
22 |
.block-mob__item--4col
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /public-portal/src/pods/design-sistem/example-components/example-buttons-and-texts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from '../styles.module.scss'; 4 | 5 | export function ExampleButtonsAndTexts() { 6 | return ( 7 |
8 |

Buttons and Links

9 |
10 | 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 | 39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/mixins/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin boldText() { 2 | font-weight: var(--fw-bold); 3 | } 4 | 5 | @mixin lightText() { 6 | font-weight: var(--fw-light); 7 | background-color: aqua; 8 | } 9 | 10 | @mixin button( 11 | $bgcolor: var(--color-primary), 12 | $textColor: var(--color-accent), 13 | $border: false, 14 | $padding: 1em, 15 | $borderRadius: var(--master-border-radius) 16 | ) { 17 | background-color: $bgcolor; 18 | font-size: var(--fs-md); 19 | color: $textColor; 20 | padding: 0.5em $padding 0.5em; 21 | border-radius: $borderRadius; 22 | font-weight: var(--fw-bold); 23 | border: none; 24 | transition: all ease 200ms; 25 | cursor: pointer; 26 | user-select: none; 27 | 28 | @if $border { 29 | border: 0.1rem solid $textColor; 30 | } 31 | 32 | &:active { 33 | transform: translateY(0.2rem); 34 | } 35 | } 36 | 37 | @mixin supports($feature, $value) { 38 | @supports (#{$feature}: #{$value}) { 39 | @content; 40 | } 41 | } 42 | 43 | @mixin hover { 44 | @media (hover: hover) { 45 | @content; 46 | } 47 | } 48 | 49 | @mixin pseudo($width, $height) { 50 | content: ''; 51 | position: absolute; 52 | width: $width; 53 | height: $height; 54 | @content; 55 | } 56 | 57 | // Maybe this is not necesary, can change brakepoints values 58 | $breakpoints: ( 59 | movil: 320px, 60 | tablet: 768px, 61 | laptop: 1020px, 62 | desktop: 1440px, 63 | ); 64 | 65 | @mixin responsive($key) { 66 | @if map-has-key($breakpoints, $key) { 67 | @media screen and (min-width: map-get($breakpoints, $key)) { 68 | @content; 69 | } 70 | } @else { 71 | @media screen and (min-width: $key) { 72 | @content; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/texts/_texts.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/mixins' as *; 2 | 3 | h1, 4 | h2, 5 | h3, 6 | h4, 7 | h5, 8 | h6 { 9 | font-family: var(--ff-headlines); 10 | font-weight: var(--fw-bold); 11 | font-optical-sizing: auto; 12 | margin-block-start: 0em; 13 | margin-block-end: 0em; 14 | } 15 | 16 | p { 17 | margin-block-start: 0em; 18 | margin-block-end: 0em; 19 | } 20 | 21 | h1 { 22 | font-weight: var(--fw-bold); 23 | font-size: var(--fs-xxxl); 24 | } 25 | 26 | h2 { 27 | font-size: var(--fs-xxl); 28 | } 29 | 30 | h3 { 31 | font-size: var(--fs-xl); 32 | } 33 | 34 | h4 { 35 | font-size: var(--fs-lg); 36 | } 37 | 38 | .hero-txt { 39 | font-weight: var(--fw-black); 40 | font-size: 3rem; 41 | } 42 | 43 | .txt-title { 44 | font-size: var(--fs-xxl); 45 | margin-block-start: 0; 46 | 47 | &--bold { 48 | @include boldText(); 49 | } 50 | 51 | &--light { 52 | @include lightText(); 53 | } 54 | } 55 | 56 | .txt-large { 57 | font-size: var(--fs-lg); 58 | 59 | &--bold { 60 | @include boldText(); 61 | } 62 | 63 | &--light { 64 | @include lightText(); 65 | } 66 | } 67 | 68 | .txt-common { 69 | font-size: var(--fs-md); 70 | 71 | &--bold { 72 | @include boldText(); 73 | } 74 | 75 | &--light { 76 | @include lightText(); 77 | } 78 | } 79 | 80 | .txt-detail { 81 | font-size: var(--fs-sm); 82 | 83 | &--bold { 84 | @include boldText(); 85 | } 86 | 87 | &--light { 88 | @include lightText(); 89 | } 90 | } 91 | 92 | .txt--primary { 93 | color: var(--color-txt-primary); 94 | } 95 | .txt--secondary { 96 | color: var(--color-txt-secondary); 97 | } 98 | .txt--contrast { 99 | color: var(--color-txt-contrast); 100 | } 101 | .txt--accent { 102 | color: var(--color-txt-accent); 103 | } 104 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/vars/_style-variables.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | /* BreakPoints */ 3 | --bkp-mobile-sm: 360px; 4 | --bkp-mobile-md: 414px; 5 | --bkp-mobile-lg: 540px; 6 | --bkp-tablet-md: 768px; 7 | --bkp-tablet-lg: 980px; 8 | --bkp-desk-sm: 1024px; 9 | --bkp-desk-md: 1200px; 10 | --bkp-desk-lg: 1440px; 11 | 12 | /* Colors */ 13 | --color-primary: #3a2e23; 14 | --color-primary-lighter: #745c46; 15 | --color-primary-darker: #0d0805; 16 | 17 | --color-secondary: #924105; 18 | --color-secondary-lighter: #cc5b07; 19 | --color-secondary-darker: #541100; 20 | 21 | --color-accent: #ffcf20; 22 | --color-accent-lighter: #ffe112; 23 | --color-accent-darker: #ff9f00; 24 | 25 | --color-txt-primary: var(--color-primary); 26 | --color-txt-secondary: var(--color-secondary); 27 | --color-txt-contrast: #ffffff; 28 | --color-txt-accent: var(--color-accent); 29 | 30 | --color-bg-primary: #fffbf3; 31 | --color-bg-primary-lighter: #fff; 32 | --color-bg-primary-darker: #fff3cf; 33 | --color-bg-contrast: var(--color-primary); 34 | --color-bg-contrast-darker: #281f17; 35 | 36 | --color-success: #22c14b; 37 | --color-success-lighter: #9ff9c4; 38 | --color-success-darker: #558953; 39 | 40 | --color-warn: #fb8510; 41 | --color-warn-lighter: #ffde99; 42 | --color-warn-darker: #c15b00; 43 | 44 | --color-error: #f32400; 45 | --color-error-lighter: #ffe5e5; 46 | --color-error-darker: #ff6565; 47 | 48 | --color-info: #3d7af3; 49 | --color-info-lighter: #dcfdfc; 50 | --color-info-darker: #0000e7; 51 | 52 | /* Texts*/ 53 | --ff-headlines: 'Oswald Variable', sans-serif; 54 | --ff-main: 'Roboto', sans-serif; 55 | 56 | --fs-master: 10px; /* -> Master font-size */ 57 | 58 | --fs-xxxl: 2.7rem; 59 | --fs-xxl: 2.4rem; 60 | --fs-xl: 2rem; 61 | --fs-lg: 1.8rem; 62 | --fs-md: 1.6rem; 63 | --fs-sm: 1.4rem; 64 | --fs-xs: 1.2rem; 65 | 66 | --fw-light: 300; 67 | --fw-normal: 400; 68 | --fw-bold: 500; 69 | --fw-black: 600; 70 | 71 | /* Spaces*/ 72 | --space-xs: 1rem; 73 | --space-sm: 1.5rem; 74 | --space-md: 2rem; 75 | --space-lg: 3rem; 76 | --space-xl: 5rem; 77 | --space-xxl: 6.5rem; 78 | 79 | --master-border-radius: 3px; 80 | } 81 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/layout/_layout.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | 3 | $spaces: ( 4 | 'xs': var(--space-xs), 5 | 'sm': var(--space-sm), 6 | 'md': var(--space-md), 7 | 'lg': var(--space-lg), 8 | 'xl': var(--space-xl), 9 | 'xxl': var(--space-xxl), 10 | ); 11 | 12 | /* Spacing classes */ 13 | @each $space, $spaceVar in $spaces { 14 | // margin 15 | .mb-#{$space} { 16 | margin-bottom: $spaceVar; 17 | } 18 | .mt-#{$space} { 19 | margin-top: $spaceVar; 20 | } 21 | .my-#{$space} { 22 | margin-top: $spaceVar; 23 | margin-bottom: $spaceVar; 24 | } 25 | .mx-#{$space} { 26 | margin-left: $spaceVar; 27 | margin-right: $spaceVar; 28 | } 29 | // padding 30 | .pb-#{$space} { 31 | padding-bottom: $spaceVar; 32 | } 33 | .pt-#{$space} { 34 | padding-top: $spaceVar; 35 | } 36 | .py-#{$space} { 37 | padding-top: $spaceVar; 38 | padding-bottom: $spaceVar; 39 | } 40 | .px-#{$space} { 41 | padding-left: $spaceVar; 42 | padding-right: $spaceVar; 43 | } 44 | } 45 | 46 | /* Standard blocks and grid system for mobile devices */ 47 | .block-mob { 48 | max-width: 100%; 49 | margin-top: var(--space-lg); 50 | margin-bottom: var(--space-lg); 51 | display: flex; 52 | flex-wrap: wrap; 53 | gap: var(--space-xs); 54 | justify-content: space-between; 55 | 56 | &__item { 57 | flex-basis: 100%; 58 | max-width: 100%; 59 | flex-grow: 1; 60 | padding: var(--space-sm); 61 | 62 | &--2col { 63 | flex-basis: calc((100% - (var(--space-xs))) / 2); 64 | padding: var(--space-sm); 65 | } 66 | 67 | &--3col { 68 | flex-basis: calc((100% - (2 * var(--space-xs))) / 3); 69 | padding: var(--space-sm); 70 | } 71 | 72 | &--4col { 73 | flex-basis: calc((100% - (3 * var(--space-xs))) / 4); 74 | padding: var(--space-sm); 75 | } 76 | } 77 | } 78 | 79 | /* Layout for beers list */ 80 | 81 | .beers-grid-layout { 82 | display: grid; 83 | grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr)); 84 | gap: var(--space-lg); 85 | list-style-type: none; 86 | padding-left: var(--space-md); 87 | padding-right: var(--space-md); 88 | 89 | & > li, 90 | & > .grid-item { 91 | display: flex; 92 | flex-wrap: wrap; 93 | background-color: var(--color-bg-primary-lighter); 94 | padding: var(--space-md); 95 | margin: 0; 96 | justify-content: center; 97 | gap: var(--space-sm); 98 | border-radius: calc(var(--master-border-radius) * 3); 99 | 100 | & > * { 101 | flex-basis: 100%; 102 | } 103 | } 104 | } 105 | 106 | .beer-img-container { 107 | max-width: 100%; 108 | text-align: center; 109 | padding-top: var(--space-md); 110 | 111 | img { 112 | max-width: 100px; 113 | } 114 | } 115 | 116 | .beer-name-and-price { 117 | display: flex; 118 | align-items: last baseline; 119 | justify-content: space-between; 120 | } 121 | 122 | /* Block Colors */ 123 | .bg-contrast { 124 | color: var(--color-txt-contrast); 125 | background-color: var(--color-bg-contrast); 126 | } 127 | 128 | .bg-primary-lighter { 129 | color: var(--color-txt-primary); 130 | background-color: var(--color-bg-primary-lighter); 131 | } 132 | .bg-primary-dark { 133 | color: var(--color-txt-primary); 134 | background-color: var(--color-bg-primary-darker); 135 | } 136 | -------------------------------------------------------------------------------- /public-portal/src/styles/default-global-theme/resets/_normalize-reset.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { 178 | /* 1 */ 179 | overflow: visible; 180 | } 181 | 182 | /** 183 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 184 | * 1. Remove the inheritance of text transform in Firefox. 185 | */ 186 | 187 | button, 188 | select { 189 | /* 1 */ 190 | text-transform: none; 191 | } 192 | 193 | /** 194 | * Correct the inability to style clickable types in iOS and Safari. 195 | */ 196 | 197 | button, 198 | [type='button'], 199 | [type='reset'], 200 | [type='submit'] { 201 | appearance: button; 202 | -webkit-appearance: button; 203 | } 204 | 205 | /** 206 | * Remove the inner border and padding in Firefox. 207 | */ 208 | 209 | button::-moz-focus-inner, 210 | [type='button']::-moz-focus-inner, 211 | [type='reset']::-moz-focus-inner, 212 | [type='submit']::-moz-focus-inner { 213 | border-style: none; 214 | padding: 0; 215 | } 216 | 217 | /** 218 | * Restore the focus styles unset by the previous rule. 219 | */ 220 | 221 | button:-moz-focusring, 222 | [type='button']:-moz-focusring, 223 | [type='reset']:-moz-focusring, 224 | [type='submit']:-moz-focusring { 225 | outline: 1px dotted ButtonText; 226 | } 227 | 228 | /** 229 | * Correct the padding in Firefox. 230 | */ 231 | 232 | fieldset { 233 | padding: 0.35em 0.75em 0.625em; 234 | } 235 | 236 | /** 237 | * 1. Correct the text wrapping in Edge and IE. 238 | * 2. Correct the color inheritance from `fieldset` elements in IE. 239 | * 3. Remove the padding so developers are not caught out when they zero out 240 | * `fieldset` elements in all browsers. 241 | */ 242 | 243 | legend { 244 | box-sizing: border-box; /* 1 */ 245 | color: inherit; /* 2 */ 246 | display: table; /* 1 */ 247 | max-width: 100%; /* 1 */ 248 | padding: 0; /* 3 */ 249 | white-space: normal; /* 1 */ 250 | } 251 | 252 | /** 253 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 254 | */ 255 | 256 | progress { 257 | vertical-align: baseline; 258 | } 259 | 260 | /** 261 | * Remove the default vertical scrollbar in IE 10+. 262 | */ 263 | 264 | textarea { 265 | overflow: auto; 266 | } 267 | 268 | /** 269 | * 1. Add the correct box sizing in IE 10. 270 | * 2. Remove the padding in IE 10. 271 | */ 272 | 273 | [type='checkbox'], 274 | [type='radio'] { 275 | box-sizing: border-box; /* 1 */ 276 | padding: 0; /* 2 */ 277 | } 278 | 279 | /** 280 | * Correct the cursor style of increment and decrement buttons in Chrome. 281 | */ 282 | 283 | [type='number']::-webkit-inner-spin-button, 284 | [type='number']::-webkit-outer-spin-button { 285 | height: auto; 286 | } 287 | 288 | /** 289 | * 1. Correct the odd appearance in Chrome and Safari. 290 | * 2. Correct the outline style in Safari. 291 | */ 292 | 293 | [type='search'] { 294 | appearance: textfield; /* 1 */ 295 | -webkit-appearance: textfield; /* 1 */ 296 | outline-offset: -2px; /* 2 */ 297 | } 298 | 299 | /** 300 | * Remove the inner padding in Chrome and Safari on macOS. 301 | */ 302 | 303 | [type='search']::-webkit-search-decoration { 304 | -webkit-appearance: none; 305 | } 306 | 307 | /** 308 | * 1. Correct the inability to style clickable types in iOS and Safari. 309 | * 2. Change font properties to `inherit` in Safari. 310 | */ 311 | 312 | ::-webkit-file-upload-button { 313 | -webkit-appearance: button; /* 1 */ 314 | font: inherit; /* 2 */ 315 | } 316 | 317 | /* Interactive 318 | ========================================================================== */ 319 | 320 | /* 321 | * Add the correct display in Edge, IE 10+, and Firefox. 322 | */ 323 | 324 | details { 325 | display: block; 326 | } 327 | 328 | /* 329 | * Add the correct display in all browsers. 330 | */ 331 | 332 | summary { 333 | display: list-item; 334 | } 335 | 336 | /* Misc 337 | ========================================================================== */ 338 | 339 | /** 340 | * Add the correct display in IE 10+. 341 | */ 342 | 343 | template { 344 | display: none; 345 | } 346 | 347 | /** 348 | * Add the correct display in IE 10. 349 | */ 350 | 351 | [hidden] { 352 | display: none; 353 | } 354 | -------------------------------------------------------------------------------- /public-portal/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /public-portal/public/iso_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | --------------------------------------------------------------------------------