[DEV] first vesion of karisic in react + chakra-ui
This commit is contained in:
parent
21d53b77f2
commit
1d4c547d89
@ -1,4 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
@ -29,7 +29,7 @@ public class WebLauncherLocal extends WebLauncher {
|
||||
TrackResource.class, DataResource.class);
|
||||
final AnalyzeApi api = new AnalyzeApi();
|
||||
api.addAllApi(listOfResources);
|
||||
TsGenerateApi.generateApi(api, "../front/src/app/back-api/");
|
||||
TsGenerateApi.generateApi(api, "../front2/src/back-api/");
|
||||
LOGGER.info("Generate APIs (DONE)");
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ public class WebLauncherLocal extends WebLauncher {
|
||||
ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/";
|
||||
//ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/";
|
||||
ConfigBaseVariable.dbPort = "3906";
|
||||
ConfigBaseVariable.testMode = "true";
|
||||
}
|
||||
try {
|
||||
super.migrateDB();
|
||||
|
@ -1,9 +1,12 @@
|
||||
package org.kar.karusic.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.filter.GenericContext;
|
||||
import org.kar.karusic.api.UserResourceModel.PartRight;
|
||||
import org.kar.karusic.api.UserResourceModel.UserMe;
|
||||
import org.kar.karusic.model.UserKarusic;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -20,7 +23,7 @@ import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
|
||||
@Path("/users")
|
||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class UserResource {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
|
||||
|
||||
@ -74,10 +77,15 @@ public class UserResource {
|
||||
@GET
|
||||
@Path("me")
|
||||
@RolesAllowed("USER")
|
||||
public UserOut getMe(@Context final SecurityContext sc) {
|
||||
public UserMe getMe(@Context final SecurityContext sc) {
|
||||
LOGGER.debug("getMe()");
|
||||
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||
LOGGER.debug("== USER ? {}", gc.userByToken);
|
||||
return new UserOut(gc.userByToken.id, gc.userByToken.name);
|
||||
return new UserMe(gc.userByToken.id, gc.userByToken.name, //
|
||||
Map.of(gc.userByToken.name, //
|
||||
Map.of("admin", PartRight.READ_WRITE, //
|
||||
"user", PartRight.READ_WRITE), //
|
||||
"karusic", //
|
||||
Map.of("user", PartRight.READ)));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package org.kar.karusic.api.UserResourceModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.kar.archidata.annotation.NoWriteSpecificMode;
|
||||
|
||||
@NoWriteSpecificMode
|
||||
public class ModuleAuthorizations extends HashMap<String, PartRight> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ModuleAuthorizations() {}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.kar.karusic.api.UserResourceModel;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
public enum PartRight {
|
||||
READ(1), //
|
||||
WRITE(2), //
|
||||
READ_WRITE(3);
|
||||
|
||||
private final int value;
|
||||
|
||||
PartRight(final int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static PartRight fromValue(final int value) {
|
||||
for (final PartRight species : PartRight.values()) {
|
||||
if (species.getValue() == value) {
|
||||
return species;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("PartRight: Unknown value: " + value);
|
||||
}
|
||||
}
|
24
back/src/org/kar/karusic/api/UserResourceModel/UserMe.java
Normal file
24
back/src/org/kar/karusic/api/UserResourceModel/UserMe.java
Normal file
@ -0,0 +1,24 @@
|
||||
package org.kar.karusic.api.UserResourceModel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.kar.archidata.annotation.NoWriteSpecificMode;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@NoWriteSpecificMode
|
||||
public class UserMe {
|
||||
public long id;
|
||||
public String login;
|
||||
@Schema(description = "Map<EntityName, Map<PartName, Right>>")
|
||||
public Map<String, Map<String, PartRight>> rights;
|
||||
|
||||
public UserMe() {}
|
||||
|
||||
public UserMe(final long id, final String login, final Map<String, Map<String, PartRight>> rights) {
|
||||
this.id = id;
|
||||
this.login = login;
|
||||
this.rights = rights;
|
||||
}
|
||||
|
||||
}
|
31
env_dev/docker-compose.yaml
Normal file
31
env_dev/docker-compose.yaml
Normal file
@ -0,0 +1,31 @@
|
||||
services:
|
||||
kar_db_service:
|
||||
image: mysql:latest
|
||||
restart: always
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=base_db_password
|
||||
volumes:
|
||||
- ./data:/var/lib/mysql
|
||||
mem_limit: 300m
|
||||
ports:
|
||||
- 3906:3306
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
# perform a 1 minute grace to let the DB to perform the initialization
|
||||
start_period: 1m
|
||||
start_interval: 1m
|
||||
|
||||
kar_adminer_service:
|
||||
image: adminer:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
kar_db_service:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- kar_db_service:db
|
||||
ports:
|
||||
- 4079:8080
|
||||
mem_limit: 50m
|
||||
|
27
front2/.storybook/main.ts
Normal file
27
front2/.storybook/main.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
builder: '@storybook/builder-vite',
|
||||
},
|
||||
|
||||
stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
|
||||
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
|
||||
|
||||
staticDirs: ['../public'],
|
||||
|
||||
typescript: {
|
||||
reactDocgen: false,
|
||||
},
|
||||
|
||||
docs: {},
|
||||
};
|
||||
|
||||
export default config;
|
16
front2/.storybook/preview-head.html
Normal file
16
front2/.storybook/preview-head.html
Normal file
@ -0,0 +1,16 @@
|
||||
<style>
|
||||
html {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.docs-story > :first-child {
|
||||
padding: 0;
|
||||
}
|
||||
.docs-story > * {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
#root #start-ui-storybook-wrapper {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
43
front2/.storybook/preview.tsx
Normal file
43
front2/.storybook/preview.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import theme from '../src/theme';
|
||||
|
||||
// .storybook/preview.js
|
||||
export const parameters = {
|
||||
options: {
|
||||
storySort: {
|
||||
order: ['StyleGuide', 'Components', 'Fields', 'App Layout'],
|
||||
},
|
||||
},
|
||||
actions: {},
|
||||
layout: 'fullscreen',
|
||||
backgrounds: { disable: true, grid: { disable: true } },
|
||||
chakra: {
|
||||
theme,
|
||||
},
|
||||
};
|
||||
|
||||
const DocumentationWrapper = ({ children }) => {
|
||||
return (
|
||||
<Box id="start-ui-storybook-wrapper" p="4" pb="8" flex="1">
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
(Story, context) => (
|
||||
<ChakraProvider theme={theme}>
|
||||
{/* Using MemoryRouter to avoid route clashing with Storybook */}
|
||||
<MemoryRouter>
|
||||
<DocumentationWrapper>
|
||||
<Story {...context} />
|
||||
</DocumentationWrapper>
|
||||
</MemoryRouter>
|
||||
</ChakraProvider>
|
||||
),
|
||||
];
|
2
front2/LICENSE
Normal file
2
front2/LICENSE
Normal file
@ -0,0 +1,2 @@
|
||||
Proprietary
|
||||
@copyright Edouard Dupin 2024
|
6
front2/app-build.json
Normal file
6
front2/app-build.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"display": "__DEVELOPMENT__",
|
||||
"version": "__VERSION__",
|
||||
"commit": "__COMMIT__",
|
||||
"date": "__DATE__"
|
||||
}
|
25
front2/build.js
Normal file
25
front2/build.js
Normal file
@ -0,0 +1,25 @@
|
||||
const dayjs = require('dayjs');
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const generateAppBuild = () => {
|
||||
const getVersion = () => fs.readFileSync('version.txt', 'utf8');
|
||||
|
||||
const commit = process.env.VERCEL_GIT_COMMIT_SHA
|
||||
? process.env.VERCEL_GIT_COMMIT_SHA
|
||||
: getVersion();
|
||||
|
||||
const appBuildContent = {
|
||||
display: `${dayjs().format('YYYY-MM-DD')}`,
|
||||
version: `${commit} - ${dayjs().format()}`,
|
||||
commit,
|
||||
date: dayjs().format(),
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
'./app-build.json',
|
||||
JSON.stringify(appBuildContent, null, 2)
|
||||
);
|
||||
};
|
||||
|
||||
generateAppBuild();
|
0
front2/doc/.keep
Normal file
0
front2/doc/.keep
Normal file
2
front2/docker/.env.production
Normal file
2
front2/docker/.env.production
Normal file
@ -0,0 +1,2 @@
|
||||
# URL for database connection
|
||||
VITE_API_BASE_URL=api/
|
103
front2/docker/prod.Dockerfile
Normal file
103
front2/docker/prod.Dockerfile
Normal file
@ -0,0 +1,103 @@
|
||||
###############################################################
|
||||
## Install dependency:
|
||||
###############################################################
|
||||
FROM node:latest AS dependency
|
||||
|
||||
# For pnpm
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# copy the credential
|
||||
COPY npmrc /root/.npmrc
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
COPY src/theme ./src/theme
|
||||
# TODO: install only the production environment:
|
||||
RUN pnpm install --prod=false
|
||||
|
||||
###############################################################
|
||||
## Install sources
|
||||
###############################################################
|
||||
FROM dependency AS load_sources
|
||||
|
||||
# JUST to get the vertion of the application and his sha...
|
||||
COPY build.js \
|
||||
version.txt \
|
||||
tsconfig.json \
|
||||
tsconfig.node.json \
|
||||
vite.config.mts \
|
||||
.env.validator.js \
|
||||
index.html \
|
||||
./
|
||||
COPY public public
|
||||
COPY src src
|
||||
|
||||
#We are not in prod mode ==> we need to overwrite the production env.
|
||||
ARG env=docker/.env.production
|
||||
COPY ${env} .env
|
||||
|
||||
###############################################################
|
||||
## Run the linter
|
||||
###############################################################
|
||||
FROM load_sources AS check
|
||||
COPY .eslintrc.json app-build.json ./
|
||||
# Run linter
|
||||
RUN pnpm lint .
|
||||
RUN pnpm tsc --noEmit
|
||||
|
||||
###############################################################
|
||||
## Run the Unit test
|
||||
###############################################################
|
||||
FROM load_sources AS unittest
|
||||
COPY vitest.config.mts app-build.json ./
|
||||
|
||||
# Run unit test
|
||||
RUN pnpm test
|
||||
|
||||
###############################################################
|
||||
## Build the story-book
|
||||
###############################################################
|
||||
FROM load_sources AS builder_storybook
|
||||
COPY app-build.json ./app-build.json
|
||||
COPY .storybook ./.storybook/
|
||||
# build the storybook in static
|
||||
RUN SKIP_ENV_VALIDATIONS=1 pnpm storybook:build
|
||||
|
||||
###############################################################
|
||||
## Build the sources
|
||||
###############################################################
|
||||
FROM load_sources AS builder
|
||||
# build in bundle mode all the application
|
||||
RUN pnpm static:build
|
||||
|
||||
|
||||
###############################################################
|
||||
## Runner environment:
|
||||
###############################################################
|
||||
FROM httpd:latest AS runner
|
||||
WORKDIR /app
|
||||
# configure HTTP server (add a redirection on the index.html to manage new app model to re-find the generic page):
|
||||
RUN sed -e '/DocumentRoot/,/Directory>/d' -i /usr/local/apache2/conf/httpd.conf
|
||||
RUN sed -r 's|#LoadModule rewrite_module|LoadModule rewrite_module|' -i /usr/local/apache2/conf/httpd.conf
|
||||
RUN echo '<VirtualHost *:80> \n\
|
||||
ServerName my-app \n\
|
||||
DocumentRoot "/usr/local/apache2/htdocs" \n\
|
||||
<Directory "/usr/local/apache2/htdocs"> \n\
|
||||
Options Indexes FollowSymLinks \n\
|
||||
AllowOverride None \n\
|
||||
Require all granted \n\
|
||||
RewriteEngine on \n\
|
||||
# Do not rewrite files or directories \n\
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR] \n\
|
||||
RewriteCond %{REQUEST_FILENAME} -d \n\
|
||||
RewriteRule ^ - [L] \n\
|
||||
# Rewrite everything else to index.html to allow HTML5 state links \n\
|
||||
RewriteRule ^ app/index.html [L] \n\
|
||||
</Directory> \n\
|
||||
</VirtualHost> \n\
|
||||
' >> /usr/local/apache2/conf/httpd.conf
|
||||
|
||||
# copy artifact build from the 'build environment'
|
||||
COPY --from=builder /app/dist /usr/local/apache2/htdocs/app
|
78
front2/ikon.svg
Normal file
78
front2/ikon.svg
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733333 67.733333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="ikon.svg"
|
||||
inkscape:export-filename="/home/heero/dev/perso/appl_pro/NoKomment/plugin/chrome/ikon.png"
|
||||
inkscape:export-xdpi="7.1250005"
|
||||
inkscape:export-ydpi="7.1250005">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="52.480467"
|
||||
inkscape:cy="138.73493"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:snap-text-baseline="false"
|
||||
inkscape:window-width="1918"
|
||||
inkscape:window-height="1038"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="20"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4504" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-229.26668)">
|
||||
<g
|
||||
aria-label="K"
|
||||
transform="scale(1.0347881,0.96638145)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.55024719px;line-height:1.25;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.11376619"
|
||||
id="text821">
|
||||
<path
|
||||
d="m 12.784421,241.62303 h 8.949095 v 27.37877 l 25.568842,-27.37877 6.39221,6.84469 -20.455074,21.90302 20.455074,27.37876 -6.39221,5.47576 -19.176632,-27.37877 -6.39221,6.84469 0,20.53408 h -8.949095 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.55024719px;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:2.11376619;fill:#ff0000;fill-opacity:1"
|
||||
id="path823"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
13
front2/index.html
Normal file
13
front2/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Karusic</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</head>
|
||||
<body className="flex flex-col">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
18
front2/knip.ts
Normal file
18
front2/knip.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import type { KnipConfig } from 'knip';
|
||||
|
||||
const config: KnipConfig = {
|
||||
// Ignoring mostly shell binaries
|
||||
ignoreBinaries: ['export', 'sleep'],
|
||||
ignore: [
|
||||
// Related to tests
|
||||
'tests/**',
|
||||
'**.conf.js',
|
||||
'steps.d.ts',
|
||||
'steps_file.js',
|
||||
'env_ci/codecept.conf.js',
|
||||
// Generic components are useful.
|
||||
'src/components/**',
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
108
front2/package.json
Normal file
108
front2/package.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"name": "karusic",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "KAR web music application",
|
||||
"author": {
|
||||
"name": "Edouard DUPIN",
|
||||
"email": "yui.heero@gmail.farm"
|
||||
},
|
||||
"license": "PROPRIETARY",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"scripts": {
|
||||
"update_packages": "ncu --upgrade",
|
||||
"install_dependency": "pnpm install",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"build": "tsc && vite build",
|
||||
"dev": "vite",
|
||||
"pretty": "prettier -w .",
|
||||
"lint": "pnpm tsc --noEmit",
|
||||
"storybook": "storybook dev -p 3001",
|
||||
"storybook:build": "storybook build && mv ./storybook-static ./public/storybook"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/anatomy": "2.2.2",
|
||||
"@chakra-ui/cli": "2.4.1",
|
||||
"@chakra-ui/react": "2.8.2",
|
||||
"@chakra-ui/theme-tools": "2.1.2",
|
||||
"@dnd-kit/core": "6.1.0",
|
||||
"@dnd-kit/modifiers": "7.0.0",
|
||||
"@dnd-kit/sortable": "8.0.0",
|
||||
"@dnd-kit/utilities": "3.2.2",
|
||||
"@emotion/react": "11.13.0",
|
||||
"@emotion/styled": "11.13.0",
|
||||
"allotment": "1.20.2",
|
||||
"css-mediaquery": "0.1.2",
|
||||
"dayjs": "1.11.12",
|
||||
"history": "5.3.0",
|
||||
"react": "18.3.1",
|
||||
"react-color-palette": "7.2.2",
|
||||
"react-currency-input-field": "3.8.0",
|
||||
"react-custom-scrollbars": "4.2.1",
|
||||
"react-day-picker": "9.0.8",
|
||||
"react-dom": "18.3.1",
|
||||
"react-error-boundary": "4.0.13",
|
||||
"react-focus-lock": "2.12.1",
|
||||
"react-icons": "5.3.0",
|
||||
"react-popper": "2.3.0",
|
||||
"react-router-dom": "6.26.1",
|
||||
"react-select": "5.8.0",
|
||||
"react-simple-keyboard": "3.7.144",
|
||||
"react-sticky-el": "2.1.0",
|
||||
"react-use": "17.5.1",
|
||||
"react-use-draggable-scroll": "0.4.7",
|
||||
"react-virtuoso": "4.10.1",
|
||||
"ts-pattern": "5.3.1",
|
||||
"uuid": "10.0.0",
|
||||
"zod": "3.23.8",
|
||||
"zustand": "4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/styled-system": "2.9.2",
|
||||
"@playwright/test": "1.46.0",
|
||||
"@storybook/addon-actions": "8.2.9",
|
||||
"@storybook/addon-essentials": "8.2.9",
|
||||
"@storybook/addon-links": "8.2.9",
|
||||
"@storybook/addon-mdx-gfm": "8.2.9",
|
||||
"@storybook/react": "8.2.9",
|
||||
"@storybook/react-vite": "8.2.9",
|
||||
"@storybook/theming": "8.2.9",
|
||||
"@testing-library/jest-dom": "6.4.8",
|
||||
"@testing-library/react": "16.0.0",
|
||||
"@testing-library/user-event": "14.5.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "4.3.0",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/node": "22.3.0",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@types/react-sticky-el": "1.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "8.1.0",
|
||||
"@typescript-eslint/parser": "8.1.0",
|
||||
"@vitejs/plugin-react": "4.3.1",
|
||||
"eslint": "9.9.0",
|
||||
"eslint-plugin-codeceptjs": "1.3.0",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-react": "7.35.0",
|
||||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"eslint-plugin-storybook": "0.8.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"knip": "5.27.2",
|
||||
"lint-staged": "15.2.9",
|
||||
"prettier": "3.3.3",
|
||||
"puppeteer": "23.1.0",
|
||||
"react-is": "18.3.1",
|
||||
"storybook": "8.2.9",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.5.4",
|
||||
"vite": "5.4.1",
|
||||
"vitest": "2.0.5",
|
||||
"npm-check-updates": "^17.0.6"
|
||||
}
|
||||
}
|
0
front2/playwright-report/.keep
Normal file
0
front2/playwright-report/.keep
Normal file
14157
front2/pnpm-lock.yaml
generated
Normal file
14157
front2/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
front2/prettier.config.js
Normal file
16
front2/prettier.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Using a JS file, allowing us to add comments
|
||||
module.exports = {
|
||||
// This plugins line is mandatory for the plugin to work with pnpm.
|
||||
// https://github.com/trivago/prettier-plugin-sort-imports/blob/61d069711008c530f5a41ca4e254781abc5de358/README.md?plain=1#L89-L96
|
||||
plugins: ['@trivago/prettier-plugin-sort-imports'],
|
||||
endOfLine: 'lf',
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'es5',
|
||||
arrowParens: 'always',
|
||||
importOrder: ['^react$', '^(?!^react$|^@/|^[./]).*', '^@/(.*)$', '^[./]'],
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ['jsx', 'typescript'],
|
||||
};
|
BIN
front2/public/favicon.ico
Normal file
BIN
front2/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
118
front2/src/App.tsx
Normal file
118
front2/src/App.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { ChakraProvider, Select } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Stack,
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { environment } from '@/environment';
|
||||
import { App as SpaApp } from '@/scene/App';
|
||||
import { USERS } from '@/service/session';
|
||||
import theme from '@/theme';
|
||||
import { hashLocalData } from '@/utils/sso';
|
||||
|
||||
const AppEnvHint = () => {
|
||||
const modal = useDisclosure();
|
||||
const [selectUserTest, setSelectUserTest] = useState<string>('NO_USER');
|
||||
//const setUser = useRightsStore((store) => store.setUser);
|
||||
const buildEnv =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'Development'
|
||||
: import.meta.env.VITE_DEV_ENV_NAME;
|
||||
const envName: Array<string> = [];
|
||||
!!buildEnv && envName.push(buildEnv);
|
||||
if (!envName.length) {
|
||||
return null;
|
||||
}
|
||||
const handleChange = (selectedOption) => {
|
||||
console.log(`SELECT: [${selectedOption.target.value}]`);
|
||||
setSelectUserTest(selectedOption.target.value);
|
||||
};
|
||||
const onClose = () => {
|
||||
modal.onClose();
|
||||
if (selectUserTest == 'NO_USER') {
|
||||
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/false/__LOGOUT__`;
|
||||
} else {
|
||||
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/true/${USERS[selectUserTest]}`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
zIndex="100000"
|
||||
position="fixed"
|
||||
top="0"
|
||||
insetStart="0"
|
||||
insetEnd="0"
|
||||
h="2px"
|
||||
bg="warning.400"
|
||||
as="button"
|
||||
cursor="pointer"
|
||||
data-test-id="devtools"
|
||||
onClick={modal.onOpen}
|
||||
>
|
||||
<Text
|
||||
position="fixed"
|
||||
top="0"
|
||||
insetStart="4"
|
||||
bg="warning.400"
|
||||
color="warning.900"
|
||||
fontSize="0.6rem"
|
||||
fontWeight="bold"
|
||||
px="10px"
|
||||
marginLeft="25%"
|
||||
borderBottomStartRadius="sm"
|
||||
borderBottomEndRadius="sm"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
{envName.join(' : ')}
|
||||
</Text>
|
||||
</Box>
|
||||
<Modal isOpen={modal.isOpen} onClose={modal.onClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Outils développeurs</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Stack>
|
||||
<Text>Utilisateur</Text>
|
||||
<Select placeholder="Select test user" onChange={handleChange}>
|
||||
{Object.keys(USERS).map((key) => (
|
||||
<option value={key} key={key}>
|
||||
{key}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</Stack>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button onClick={onClose}>Apply</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<AppEnvHint />
|
||||
<SpaApp />
|
||||
</ChakraProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
66
front2/src/assets/images/avatar_generic.svg
Normal file
66
front2/src/assets/images/avatar_generic.svg
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="499"
|
||||
height="498"
|
||||
viewBox="0 0 499 498"
|
||||
version="1.1"
|
||||
id="svg10"
|
||||
sodipodi:docname="avatar_generic.svg"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs14" />
|
||||
<sodipodi:namedview
|
||||
id="namedview12"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.5060241"
|
||||
inkscape:cx="344.97766"
|
||||
inkscape:cy="288.78866"
|
||||
inkscape:window-width="3838"
|
||||
inkscape:window-height="2118"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="20"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10" />
|
||||
<rect
|
||||
style="fill:#4c83e5;fill-opacity:1;stroke:none;stroke-width:8.82841587;stroke-opacity:1"
|
||||
width="500"
|
||||
height="500"
|
||||
x="0"
|
||||
y="-1.9999847"
|
||||
id="rect2" />
|
||||
<path
|
||||
style="fill:#4e4e4d;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||
d="m 198.78572,292.40268 -11.97951,28.10434 -7.03868,-39.62542 -32.77188,20.51375 14.30166,-59.1095 -47.70972,20.8692 30.95257,-58.44261 -40.08325,-11.9709 50.99682,-31.08004 -30.32488,-33.92052 41.38608,6.88643 c 0,0 -27.42157,-58.130582 -26.08007,-58.130582 1.34149,0 54.85161,37.962212 54.85161,37.962212 l 6.10019,-52.959427 22.55992,46.026947 33.20732,-51.718401 8.75817,54.113371 53.78031,-55.134502 -14.88381,76.635492 112.00146,-17.67965 -84.26404,54.10353 65.61018,10.26713 -53.91421,37.51917 40.05564,55.00796 -51.48529,-23.57551 7.49544,56.99322 -27.79947,-24.64556 -3.80452,36.62241 -23.37869,-22.14564 -5.89389,23.82189 -20.64297,-29.48769 -15.46209,46.92367 -7.2608,-54.46889 -21.32424,51.07849 z"
|
||||
id="path3162"
|
||||
sodipodi:nodetypes="cccccccccccsccccccccccccccccccccccc" />
|
||||
<path
|
||||
style="fill:#fbd0a6;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 126.90172,351.92657 55.90379,145.23242 168.77912,0.48055 8.10502,-147.83696 -56.19339,-10.73582 -9.91368,3.09728 -8.25753,-48.27446 -8.77147,-41.82541 -73.97306,-0.86753 -4.65072,84.85557 z"
|
||||
id="path3467"
|
||||
sodipodi:nodetypes="ccccccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 45.319305,524.24985 50.878125,-168.64351 91.02866,-21.85078 124.81002,189.43887 15.4976,-12.33208 -130.63444,-191.36234 -9.67318,14.25555 124.81002,189.43887"
|
||||
id="path275"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 443.88287,524.02559 393.00474,355.38208 301.97608,333.5313 177.16606,522.97017 161.66846,510.63809 292.3029,319.27575 301.97608,333.5313 177.16606,522.97017"
|
||||
id="path275-6"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:#fbd0a6;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 187.52213,139.10279 14.34593,25.22547 9.57434,-32.06638 13.85516,33.79118 18.44245,-38.89028 4.92331,44.11511 20.28515,-38.50102 -2.21466,39.35905 31.27764,-28.90273 -5.47875,45.83312 23.27252,-10.25342 -8.67174,30.59353 c 24.86464,-33.77835 23.21015,12.27629 3.94365,35.3922 l -12.95127,26.98572 -12.80079,22.10524 -26.61623,18.28625 -53.44338,-20.79546 c -21.13665,-20.42844 -23.443,-25.48798 -31.95313,-51.61993 -19.36867,-32.18928 -17.20493,-59.66994 4.27858,-38.00437 l -2.30548,-27.96686 11.07502,10.22035 -14.60569,-33.15484 22.1057,18.70679 z"
|
||||
id="path9531"
|
||||
sodipodi:nodetypes="cccccccccccccccccccccccc" />
|
||||
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
250
front2/src/back-api/api/album-resource.ts
Normal file
250
front2/src/back-api/api/album-resource.ts
Normal file
@ -0,0 +1,250 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTCallbacks,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
RESTRequestVoid,
|
||||
} from "../rest-tools";
|
||||
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Album,
|
||||
AlbumWrite,
|
||||
Long,
|
||||
UUID,
|
||||
ZodAlbum,
|
||||
isAlbum,
|
||||
} from "../model";
|
||||
|
||||
export namespace AlbumResource {
|
||||
|
||||
/**
|
||||
* Add a Track on a specific album
|
||||
*/
|
||||
export function addTrack({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
trackId: Long,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}/track/{trackId}",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isAlbum);
|
||||
};
|
||||
/**
|
||||
* Get a specific Album with his ID
|
||||
*/
|
||||
export function get({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isAlbum);
|
||||
};
|
||||
|
||||
export const ZodGetsTypeReturn = zod.array(ZodAlbum);
|
||||
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
|
||||
|
||||
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
|
||||
try {
|
||||
ZodGetsTypeReturn.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the available Albums
|
||||
*/
|
||||
export function gets({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<GetsTypeReturn> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isGetsTypeReturn);
|
||||
};
|
||||
/**
|
||||
* Update a specific album
|
||||
*/
|
||||
export function patch({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: AlbumWrite,
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}",
|
||||
requestType: HTTPRequestModel.PATCH,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}, isAlbum);
|
||||
};
|
||||
/**
|
||||
* Add an album (when all the data already exist)
|
||||
*/
|
||||
export function post({
|
||||
restConfig,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: AlbumWrite,
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
}, isAlbum);
|
||||
};
|
||||
/**
|
||||
* Remove a specific album
|
||||
*/
|
||||
export function remove({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<void> {
|
||||
return RESTRequestVoid({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Remove a cover on a specific album
|
||||
*/
|
||||
export function removeCover({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
coverId: UUID,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}/cover/{coverId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isAlbum);
|
||||
};
|
||||
/**
|
||||
* Remove a Track on a specific album
|
||||
*/
|
||||
export function removeTrack({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
trackId: Long,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}/track/{trackId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isAlbum);
|
||||
};
|
||||
/**
|
||||
* Add a cover on a specific album
|
||||
*/
|
||||
export function uploadCover({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
file: File,
|
||||
},
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/album/{id}/cover",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}, isAlbum);
|
||||
};
|
||||
}
|
181
front2/src/back-api/api/artist-resource.ts
Normal file
181
front2/src/back-api/api/artist-resource.ts
Normal file
@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTCallbacks,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
RESTRequestVoid,
|
||||
} from "../rest-tools";
|
||||
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Artist,
|
||||
ArtistWrite,
|
||||
Long,
|
||||
UUID,
|
||||
ZodArtist,
|
||||
isArtist,
|
||||
} from "../model";
|
||||
|
||||
export namespace ArtistResource {
|
||||
|
||||
export function get({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/{id}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isArtist);
|
||||
};
|
||||
|
||||
export const ZodGetsTypeReturn = zod.array(ZodArtist);
|
||||
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
|
||||
|
||||
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
|
||||
try {
|
||||
ZodGetsTypeReturn.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function gets({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<GetsTypeReturn> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isGetsTypeReturn);
|
||||
};
|
||||
export function patch({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: ArtistWrite,
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/{id}",
|
||||
requestType: HTTPRequestModel.PATCH,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}, isArtist);
|
||||
};
|
||||
export function post({
|
||||
restConfig,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: ArtistWrite,
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
}, isArtist);
|
||||
};
|
||||
export function remove({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<void> {
|
||||
return RESTRequestVoid({
|
||||
restModel: {
|
||||
endPoint: "/artist/{id}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
});
|
||||
};
|
||||
export function removeCover({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
coverId: UUID,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/{id}/cover/{coverId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isArtist);
|
||||
};
|
||||
export function uploadCover({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
file: File,
|
||||
},
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/{id}/cover",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}, isArtist);
|
||||
};
|
||||
}
|
128
front2/src/back-api/api/data-resource.ts
Normal file
128
front2/src/back-api/api/data-resource.ts
Normal file
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
RESTRequestVoid,
|
||||
} from "../rest-tools";
|
||||
|
||||
import {
|
||||
UUID,
|
||||
} from "../model";
|
||||
|
||||
export namespace DataResource {
|
||||
|
||||
/**
|
||||
* Get back some data from the data environment (with a beautiful name (permit download with basic name)
|
||||
*/
|
||||
export function retrieveDataFull({
|
||||
restConfig,
|
||||
queries,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
queries: {
|
||||
Authorization?: string,
|
||||
},
|
||||
params: {
|
||||
name: string,
|
||||
uuid: UUID,
|
||||
},
|
||||
data: string,
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/data/{uuid}/{name}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
data,
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Get back some data from the data environment
|
||||
*/
|
||||
export function retrieveDataId({
|
||||
restConfig,
|
||||
queries,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
queries: {
|
||||
Authorization?: string,
|
||||
},
|
||||
params: {
|
||||
uuid: UUID,
|
||||
},
|
||||
data: string,
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/data/{uuid}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
data,
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Get a thumbnail of from the data environment (if resize is possible)
|
||||
*/
|
||||
export function retrieveDataThumbnailId({
|
||||
restConfig,
|
||||
queries,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
queries: {
|
||||
Authorization?: string,
|
||||
},
|
||||
params: {
|
||||
uuid: UUID,
|
||||
},
|
||||
data: string,
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/data/thumbnail/{uuid}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
data,
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Insert a new data in the data environment
|
||||
*/
|
||||
export function uploadFile({
|
||||
restConfig,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: {
|
||||
file: File,
|
||||
},
|
||||
}): Promise<void> {
|
||||
return RESTRequestVoid({
|
||||
restModel: {
|
||||
endPoint: "/data//upload/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
});
|
||||
};
|
||||
}
|
6
front2/src/back-api/api/front.ts
Normal file
6
front2/src/back-api/api/front.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
export namespace Front {
|
||||
|
||||
}
|
181
front2/src/back-api/api/gender-resource.ts
Normal file
181
front2/src/back-api/api/gender-resource.ts
Normal file
@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTCallbacks,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
RESTRequestVoid,
|
||||
} from "../rest-tools";
|
||||
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Gender,
|
||||
GenderWrite,
|
||||
Long,
|
||||
UUID,
|
||||
ZodGender,
|
||||
isGender,
|
||||
} from "../model";
|
||||
|
||||
export namespace GenderResource {
|
||||
|
||||
export function get({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/gender/{id}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isGender);
|
||||
};
|
||||
|
||||
export const ZodGetsTypeReturn = zod.array(ZodGender);
|
||||
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
|
||||
|
||||
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
|
||||
try {
|
||||
ZodGetsTypeReturn.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function gets({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<GetsTypeReturn> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/gender/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isGetsTypeReturn);
|
||||
};
|
||||
export function patch({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: GenderWrite,
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/gender/{id}",
|
||||
requestType: HTTPRequestModel.PATCH,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}, isGender);
|
||||
};
|
||||
export function post({
|
||||
restConfig,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: GenderWrite,
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/gender/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
}, isGender);
|
||||
};
|
||||
export function remove({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<void> {
|
||||
return RESTRequestVoid({
|
||||
restModel: {
|
||||
endPoint: "/gender/{id}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
});
|
||||
};
|
||||
export function removeCover({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
coverId: UUID,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/gender/{id}/cover/{coverId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isGender);
|
||||
};
|
||||
export function uploadCover({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
file: File,
|
||||
},
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/gender/{id}/cover",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}, isGender);
|
||||
};
|
||||
}
|
32
front2/src/back-api/api/health-check.ts
Normal file
32
front2/src/back-api/api/health-check.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
} from "../rest-tools";
|
||||
|
||||
import {
|
||||
HealthResult,
|
||||
isHealthResult,
|
||||
} from "../model";
|
||||
|
||||
export namespace HealthCheck {
|
||||
|
||||
export function getHealth({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<HealthResult> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/health_check/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isHealthResult);
|
||||
};
|
||||
}
|
12
front2/src/back-api/api/index.ts
Normal file
12
front2/src/back-api/api/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
export * from "./album-resource"
|
||||
export * from "./artist-resource"
|
||||
export * from "./data-resource"
|
||||
export * from "./front"
|
||||
export * from "./gender-resource"
|
||||
export * from "./health-check"
|
||||
export * from "./playlist-resource"
|
||||
export * from "./track-resource"
|
||||
export * from "./user-resource"
|
219
front2/src/back-api/api/playlist-resource.ts
Normal file
219
front2/src/back-api/api/playlist-resource.ts
Normal file
@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
RESTRequestVoid,
|
||||
} from "../rest-tools";
|
||||
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Long,
|
||||
Playlist,
|
||||
PlaylistWrite,
|
||||
UUID,
|
||||
ZodPlaylist,
|
||||
isPlaylist,
|
||||
} from "../model";
|
||||
|
||||
export namespace PlaylistResource {
|
||||
|
||||
export function addTrack({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
trackId: Long,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}/track/{trackId}",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isPlaylist);
|
||||
};
|
||||
export function get({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isPlaylist);
|
||||
};
|
||||
|
||||
export const ZodGetsTypeReturn = zod.array(ZodPlaylist);
|
||||
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
|
||||
|
||||
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
|
||||
try {
|
||||
ZodGetsTypeReturn.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function gets({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<GetsTypeReturn> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isGetsTypeReturn);
|
||||
};
|
||||
export function patch({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: PlaylistWrite,
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}",
|
||||
requestType: HTTPRequestModel.PATCH,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}, isPlaylist);
|
||||
};
|
||||
export function post({
|
||||
restConfig,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: PlaylistWrite,
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
}, isPlaylist);
|
||||
};
|
||||
export function remove({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<void> {
|
||||
return RESTRequestVoid({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
});
|
||||
};
|
||||
export function removeCover({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
coverId: UUID,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}/cover/{coverId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isPlaylist);
|
||||
};
|
||||
export function removeTrack({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
trackId: Long,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}/track/{trackId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isPlaylist);
|
||||
};
|
||||
export function uploadCover({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
file: File,
|
||||
},
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/playlist/{id}/cover",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}, isPlaylist);
|
||||
};
|
||||
}
|
252
front2/src/back-api/api/track-resource.ts
Normal file
252
front2/src/back-api/api/track-resource.ts
Normal file
@ -0,0 +1,252 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTCallbacks,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
RESTRequestVoid,
|
||||
} from "../rest-tools";
|
||||
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Long,
|
||||
Track,
|
||||
TrackWrite,
|
||||
UUID,
|
||||
ZodTrack,
|
||||
isTrack,
|
||||
} from "../model";
|
||||
|
||||
export namespace TrackResource {
|
||||
|
||||
export function addTrack({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
artistId: Long,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}/artist/{artistId}",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isTrack);
|
||||
};
|
||||
export function get({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isTrack);
|
||||
};
|
||||
|
||||
export const ZodGetsTypeReturn = zod.array(ZodTrack);
|
||||
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
|
||||
|
||||
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
|
||||
try {
|
||||
ZodGetsTypeReturn.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function gets({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<GetsTypeReturn> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isGetsTypeReturn);
|
||||
};
|
||||
export function patch({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: TrackWrite,
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}",
|
||||
requestType: HTTPRequestModel.PATCH,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
}, isTrack);
|
||||
};
|
||||
export function post({
|
||||
restConfig,
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: TrackWrite,
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
}, isTrack);
|
||||
};
|
||||
export function remove({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<void> {
|
||||
return RESTRequestVoid({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
});
|
||||
};
|
||||
export function removeCover({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
coverId: UUID,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}/cover/{coverId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isTrack);
|
||||
};
|
||||
export function removeTrack({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
artistId: Long,
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}/artist/{trackId}",
|
||||
requestType: HTTPRequestModel.DELETE,
|
||||
contentType: HTTPMimeType.TEXT_PLAIN,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isTrack);
|
||||
};
|
||||
export function uploadCover({
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
file: File,
|
||||
},
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/{id}/cover",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callbacks,
|
||||
}, isTrack);
|
||||
};
|
||||
export function uploadTrack({
|
||||
restConfig,
|
||||
data,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: {
|
||||
fileName: string,
|
||||
file: File,
|
||||
gender: string,
|
||||
artist: string,
|
||||
album: string,
|
||||
trackId: Long,
|
||||
title: string,
|
||||
},
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/track/upload/",
|
||||
requestType: HTTPRequestModel.POST,
|
||||
contentType: HTTPMimeType.MULTIPART,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
callbacks,
|
||||
}, isTrack);
|
||||
};
|
||||
}
|
84
front2/src/back-api/api/user-resource.ts
Normal file
84
front2/src/back-api/api/user-resource.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import {
|
||||
HTTPMimeType,
|
||||
HTTPRequestModel,
|
||||
RESTConfig,
|
||||
RESTRequestJson,
|
||||
} from "../rest-tools";
|
||||
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Long,
|
||||
UserKarusic,
|
||||
UserMe,
|
||||
ZodUserKarusic,
|
||||
isUserKarusic,
|
||||
isUserMe,
|
||||
} from "../model";
|
||||
|
||||
export namespace UserResource {
|
||||
|
||||
export function get({
|
||||
restConfig,
|
||||
params,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
}): Promise<UserKarusic> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/users/{id}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
params,
|
||||
}, isUserKarusic);
|
||||
};
|
||||
export function getMe({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<UserMe> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/users/me",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isUserMe);
|
||||
};
|
||||
|
||||
export const ZodGetsTypeReturn = zod.array(ZodUserKarusic);
|
||||
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
|
||||
|
||||
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
|
||||
try {
|
||||
ZodGetsTypeReturn.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function gets({
|
||||
restConfig,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
}): Promise<GetsTypeReturn> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/users/",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
restConfig,
|
||||
}, isGetsTypeReturn);
|
||||
};
|
||||
}
|
7
front2/src/back-api/index.ts
Normal file
7
front2/src/back-api/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
export * from "./model";
|
||||
export * from "./api";
|
||||
export * from "./rest-tools";
|
||||
|
53
front2/src/back-api/model/album.ts
Normal file
53
front2/src/back-api/model/album.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodLocalDate} from "./local-date";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodAlbum = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().max(256).optional(),
|
||||
description: zod.string().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).optional(),
|
||||
publication: ZodLocalDate.optional(),
|
||||
|
||||
});
|
||||
|
||||
export type Album = zod.infer<typeof ZodAlbum>;
|
||||
|
||||
export function isAlbum(data: any): data is Album {
|
||||
try {
|
||||
ZodAlbum.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodAlbum' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodAlbumWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
name: zod.string().max(256).nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
publication: ZodLocalDate.nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type AlbumWrite = zod.infer<typeof ZodAlbumWrite>;
|
||||
|
||||
export function isAlbumWrite(data: any): data is AlbumWrite {
|
||||
try {
|
||||
ZodAlbumWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodAlbumWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
59
front2/src/back-api/model/artist.ts
Normal file
59
front2/src/back-api/model/artist.ts
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodLocalDate} from "./local-date";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodArtist = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().max(256).optional(),
|
||||
description: zod.string().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).optional(),
|
||||
firstName: zod.string().max(256).optional(),
|
||||
surname: zod.string().max(256).optional(),
|
||||
birth: ZodLocalDate.optional(),
|
||||
death: ZodLocalDate.optional(),
|
||||
|
||||
});
|
||||
|
||||
export type Artist = zod.infer<typeof ZodArtist>;
|
||||
|
||||
export function isArtist(data: any): data is Artist {
|
||||
try {
|
||||
ZodArtist.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodArtist' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodArtistWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
name: zod.string().max(256).nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
firstName: zod.string().max(256).nullable().optional(),
|
||||
surname: zod.string().max(256).nullable().optional(),
|
||||
birth: ZodLocalDate.nullable().optional(),
|
||||
death: ZodLocalDate.nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type ArtistWrite = zod.infer<typeof ZodArtistWrite>;
|
||||
|
||||
export function isArtistWrite(data: any): data is ArtistWrite {
|
||||
try {
|
||||
ZodArtistWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodArtistWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
50
front2/src/back-api/model/gender.ts
Normal file
50
front2/src/back-api/model/gender.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodGender = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().max(256).optional(),
|
||||
description: zod.string().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).optional(),
|
||||
|
||||
});
|
||||
|
||||
export type Gender = zod.infer<typeof ZodGender>;
|
||||
|
||||
export function isGender(data: any): data is Gender {
|
||||
try {
|
||||
ZodGender.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGender' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenderWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
name: zod.string().max(256).nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type GenderWrite = zod.infer<typeof ZodGenderWrite>;
|
||||
|
||||
export function isGenderWrite(data: any): data is GenderWrite {
|
||||
try {
|
||||
ZodGenderWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenderWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
41
front2/src/back-api/model/generic-data-soft-delete.ts
Normal file
41
front2/src/back-api/model/generic-data-soft-delete.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodGenericData, ZodGenericDataWrite } from "./generic-data";
|
||||
|
||||
export const ZodGenericDataSoftDelete = ZodGenericData.extend({
|
||||
/**
|
||||
* Deleted state
|
||||
*/
|
||||
deleted: zod.boolean().readonly().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type GenericDataSoftDelete = zod.infer<typeof ZodGenericDataSoftDelete>;
|
||||
|
||||
export function isGenericDataSoftDelete(data: any): data is GenericDataSoftDelete {
|
||||
try {
|
||||
ZodGenericDataSoftDelete.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataSoftDelete' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericDataSoftDeleteWrite = ZodGenericDataWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type GenericDataSoftDeleteWrite = zod.infer<typeof ZodGenericDataSoftDeleteWrite>;
|
||||
|
||||
export function isGenericDataSoftDeleteWrite(data: any): data is GenericDataSoftDeleteWrite {
|
||||
try {
|
||||
ZodGenericDataSoftDeleteWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
42
front2/src/back-api/model/generic-data.ts
Normal file
42
front2/src/back-api/model/generic-data.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodGenericTiming, ZodGenericTimingWrite } from "./generic-timing";
|
||||
|
||||
export const ZodGenericData = ZodGenericTiming.extend({
|
||||
/**
|
||||
* Unique Id of the object
|
||||
*/
|
||||
id: ZodLong.readonly(),
|
||||
|
||||
});
|
||||
|
||||
export type GenericData = zod.infer<typeof ZodGenericData>;
|
||||
|
||||
export function isGenericData(data: any): data is GenericData {
|
||||
try {
|
||||
ZodGenericData.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericData' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericDataWrite = ZodGenericTimingWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type GenericDataWrite = zod.infer<typeof ZodGenericDataWrite>;
|
||||
|
||||
export function isGenericDataWrite(data: any): data is GenericDataWrite {
|
||||
try {
|
||||
ZodGenericDataWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
45
front2/src/back-api/model/generic-timing.ts
Normal file
45
front2/src/back-api/model/generic-timing.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodIsoDate} from "./iso-date";
|
||||
|
||||
export const ZodGenericTiming = zod.object({
|
||||
/**
|
||||
* Create time of the object
|
||||
*/
|
||||
createdAt: ZodIsoDate.readonly().optional(),
|
||||
/**
|
||||
* When update the object
|
||||
*/
|
||||
updatedAt: ZodIsoDate.readonly().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type GenericTiming = zod.infer<typeof ZodGenericTiming>;
|
||||
|
||||
export function isGenericTiming(data: any): data is GenericTiming {
|
||||
try {
|
||||
ZodGenericTiming.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericTiming' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericTimingWrite = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type GenericTimingWrite = zod.infer<typeof ZodGenericTimingWrite>;
|
||||
|
||||
export function isGenericTimingWrite(data: any): data is GenericTimingWrite {
|
||||
try {
|
||||
ZodGenericTimingWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericTimingWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
36
front2/src/back-api/model/health-result.ts
Normal file
36
front2/src/back-api/model/health-result.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodHealthResult = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type HealthResult = zod.infer<typeof ZodHealthResult>;
|
||||
|
||||
export function isHealthResult(data: any): data is HealthResult {
|
||||
try {
|
||||
ZodHealthResult.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodHealthResult' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodHealthResultWrite = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type HealthResultWrite = zod.infer<typeof ZodHealthResultWrite>;
|
||||
|
||||
export function isHealthResultWrite(data: any): data is HealthResultWrite {
|
||||
try {
|
||||
ZodHealthResultWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodHealthResultWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
23
front2/src/back-api/model/index.ts
Normal file
23
front2/src/back-api/model/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
export * from "./album"
|
||||
export * from "./artist"
|
||||
export * from "./gender"
|
||||
export * from "./generic-data"
|
||||
export * from "./generic-data-soft-delete"
|
||||
export * from "./generic-timing"
|
||||
export * from "./health-result"
|
||||
export * from "./int"
|
||||
export * from "./iso-date"
|
||||
export * from "./local-date"
|
||||
export * from "./long"
|
||||
export * from "./part-right"
|
||||
export * from "./playlist"
|
||||
export * from "./rest-error-response"
|
||||
export * from "./timestamp"
|
||||
export * from "./track"
|
||||
export * from "./user"
|
||||
export * from "./user-karusic"
|
||||
export * from "./user-me"
|
||||
export * from "./uuid"
|
36
front2/src/back-api/model/int.ts
Normal file
36
front2/src/back-api/model/int.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const Zodint = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type int = zod.infer<typeof Zodint>;
|
||||
|
||||
export function isint(data: any): data is int {
|
||||
try {
|
||||
Zodint.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='Zodint' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodintWrite = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type intWrite = zod.infer<typeof ZodintWrite>;
|
||||
|
||||
export function isintWrite(data: any): data is intWrite {
|
||||
try {
|
||||
ZodintWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodintWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
8
front2/src/back-api/model/iso-date.ts
Normal file
8
front2/src/back-api/model/iso-date.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodIsoDate = zod.string().datetime({ precision: 3 });
|
||||
export type IsoDate = zod.infer<typeof ZodIsoDate>;
|
8
front2/src/back-api/model/local-date.ts
Normal file
8
front2/src/back-api/model/local-date.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodLocalDate = zod.string().date();
|
||||
export type LocalDate = zod.infer<typeof ZodLocalDate>;
|
8
front2/src/back-api/model/long.ts
Normal file
8
front2/src/back-api/model/long.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodLong = zod.number();
|
||||
export type Long = zod.infer<typeof ZodLong>;
|
23
front2/src/back-api/model/part-right.ts
Normal file
23
front2/src/back-api/model/part-right.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export enum PartRight {
|
||||
READ = 1,
|
||||
WRITE = 2,
|
||||
READ_WRITE = 3,
|
||||
};
|
||||
|
||||
export const ZodPartRight = zod.nativeEnum(PartRight);
|
||||
|
||||
export function isPartRight(data: any): data is PartRight {
|
||||
try {
|
||||
ZodPartRight.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodPartRight' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
53
front2/src/back-api/model/playlist.ts
Normal file
53
front2/src/back-api/model/playlist.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodPlaylist = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().max(256).optional(),
|
||||
description: zod.string().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).optional(),
|
||||
tracks: zod.array(ZodLong),
|
||||
|
||||
});
|
||||
|
||||
export type Playlist = zod.infer<typeof ZodPlaylist>;
|
||||
|
||||
export function isPlaylist(data: any): data is Playlist {
|
||||
try {
|
||||
ZodPlaylist.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodPlaylist' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodPlaylistWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
name: zod.string().max(256).nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
tracks: zod.array(ZodLong).optional(),
|
||||
|
||||
});
|
||||
|
||||
export type PlaylistWrite = zod.infer<typeof ZodPlaylistWrite>;
|
||||
|
||||
export function isPlaylistWrite(data: any): data is PlaylistWrite {
|
||||
try {
|
||||
ZodPlaylistWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodPlaylistWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
29
front2/src/back-api/model/rest-error-response.ts
Normal file
29
front2/src/back-api/model/rest-error-response.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {Zodint, ZodintWrite } from "./int";
|
||||
|
||||
export const ZodRestErrorResponse = zod.object({
|
||||
uuid: ZodUUID.optional(),
|
||||
name: zod.string(),
|
||||
message: zod.string(),
|
||||
time: zod.string(),
|
||||
status: Zodint,
|
||||
statusMessage: zod.string(),
|
||||
|
||||
});
|
||||
|
||||
export type RestErrorResponse = zod.infer<typeof ZodRestErrorResponse>;
|
||||
|
||||
export function isRestErrorResponse(data: any): data is RestErrorResponse {
|
||||
try {
|
||||
ZodRestErrorResponse.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodRestErrorResponse' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
8
front2/src/back-api/model/timestamp.ts
Normal file
8
front2/src/back-api/model/timestamp.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodTimestamp = zod.string().datetime({ precision: 3 });
|
||||
export type Timestamp = zod.infer<typeof ZodTimestamp>;
|
61
front2/src/back-api/model/track.ts
Normal file
61
front2/src/back-api/model/track.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodTrack = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().max(256).optional(),
|
||||
description: zod.string().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).optional(),
|
||||
genderId: ZodLong.optional(),
|
||||
albumId: ZodLong.optional(),
|
||||
track: ZodLong.optional(),
|
||||
dataId: ZodUUID.optional(),
|
||||
artists: zod.array(ZodLong),
|
||||
|
||||
});
|
||||
|
||||
export type Track = zod.infer<typeof ZodTrack>;
|
||||
|
||||
export function isTrack(data: any): data is Track {
|
||||
try {
|
||||
ZodTrack.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodTrack' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodTrackWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
name: zod.string().max(256).nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
genderId: ZodLong.nullable().optional(),
|
||||
albumId: ZodLong.nullable().optional(),
|
||||
track: ZodLong.nullable().optional(),
|
||||
dataId: ZodUUID.nullable().optional(),
|
||||
artists: zod.array(ZodLong).optional(),
|
||||
|
||||
});
|
||||
|
||||
export type TrackWrite = zod.infer<typeof ZodTrackWrite>;
|
||||
|
||||
export function isTrackWrite(data: any): data is TrackWrite {
|
||||
try {
|
||||
ZodTrackWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodTrackWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
37
front2/src/back-api/model/user-karusic.ts
Normal file
37
front2/src/back-api/model/user-karusic.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUser, ZodUserWrite } from "./user";
|
||||
|
||||
export const ZodUserKarusic = ZodUser.extend({
|
||||
|
||||
});
|
||||
|
||||
export type UserKarusic = zod.infer<typeof ZodUserKarusic>;
|
||||
|
||||
export function isUserKarusic(data: any): data is UserKarusic {
|
||||
try {
|
||||
ZodUserKarusic.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserKarusic' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserKarusicWrite = ZodUserWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type UserKarusicWrite = zod.infer<typeof ZodUserKarusicWrite>;
|
||||
|
||||
export function isUserKarusicWrite(data: any): data is UserKarusicWrite {
|
||||
try {
|
||||
ZodUserKarusicWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserKarusicWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
29
front2/src/back-api/model/user-me.ts
Normal file
29
front2/src/back-api/model/user-me.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodPartRight} from "./part-right";
|
||||
|
||||
export const ZodUserMe = zod.object({
|
||||
id: ZodLong,
|
||||
login: zod.string().max(255).optional(),
|
||||
/**
|
||||
* Map<EntityName, Map<PartName, Right>>
|
||||
*/
|
||||
rights: zod.record(zod.string(), zod.record(zod.string(), ZodPartRight)),
|
||||
|
||||
});
|
||||
|
||||
export type UserMe = zod.infer<typeof ZodUserMe>;
|
||||
|
||||
export function isUserMe(data: any): data is UserMe {
|
||||
try {
|
||||
ZodUserMe.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserMe' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
41
front2/src/back-api/model/user-out.ts
Normal file
41
front2/src/back-api/model/user-out.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
|
||||
export const ZodUserOut = zod.object({
|
||||
id: ZodLong,
|
||||
login: zod.string().max(255).optional(),
|
||||
|
||||
});
|
||||
|
||||
export type UserOut = zod.infer<typeof ZodUserOut>;
|
||||
|
||||
export function isUserOut(data: any): data is UserOut {
|
||||
try {
|
||||
ZodUserOut.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserOut' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserOutWrite = zod.object({
|
||||
id: ZodLong,
|
||||
login: zod.string().max(255).nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type UserOutWrite = zod.infer<typeof ZodUserOutWrite>;
|
||||
|
||||
export function isUserOutWrite(data: any): data is UserOutWrite {
|
||||
try {
|
||||
ZodUserOutWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserOutWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
57
front2/src/back-api/model/user.ts
Normal file
57
front2/src/back-api/model/user.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodTimestamp} from "./timestamp";
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodUser = ZodGenericDataSoftDelete.extend({
|
||||
login: zod.string().max(128).optional(),
|
||||
lastConnection: ZodTimestamp.optional(),
|
||||
admin: zod.boolean(),
|
||||
blocked: zod.boolean(),
|
||||
removed: zod.boolean(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).optional(),
|
||||
|
||||
});
|
||||
|
||||
export type User = zod.infer<typeof ZodUser>;
|
||||
|
||||
export function isUser(data: any): data is User {
|
||||
try {
|
||||
ZodUser.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUser' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
login: zod.string().max(128).nullable().optional(),
|
||||
lastConnection: ZodTimestamp.nullable().optional(),
|
||||
admin: zod.boolean(),
|
||||
blocked: zod.boolean(),
|
||||
removed: zod.boolean(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type UserWrite = zod.infer<typeof ZodUserWrite>;
|
||||
|
||||
export function isUserWrite(data: any): data is UserWrite {
|
||||
try {
|
||||
ZodUserWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
8
front2/src/back-api/model/uuid.ts
Normal file
8
front2/src/back-api/model/uuid.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodUUID = zod.string().uuid();
|
||||
export type UUID = zod.infer<typeof ZodUUID>;
|
445
front2/src/back-api/rest-tools.ts
Normal file
445
front2/src/back-api/rest-tools.ts
Normal file
@ -0,0 +1,445 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2024, Edouard DUPIN, all right reserved
|
||||
* @license MPL-2
|
||||
*/
|
||||
|
||||
import { RestErrorResponse, isRestErrorResponse } from "./model";
|
||||
|
||||
export enum HTTPRequestModel {
|
||||
DELETE = "DELETE",
|
||||
GET = "GET",
|
||||
PATCH = "PATCH",
|
||||
POST = "POST",
|
||||
PUT = "PUT",
|
||||
}
|
||||
export enum HTTPMimeType {
|
||||
ALL = "*/*",
|
||||
CSV = "text/csv",
|
||||
IMAGE = "image/*",
|
||||
IMAGE_JPEG = "image/jpeg",
|
||||
IMAGE_PNG = "image/png",
|
||||
JSON = "application/json",
|
||||
MULTIPART = "multipart/form-data",
|
||||
OCTET_STREAM = "application/octet-stream",
|
||||
TEXT_PLAIN = "text/plain",
|
||||
}
|
||||
|
||||
export interface RESTConfig {
|
||||
// base of the server: http(s)://my.server.org/plop/api/
|
||||
server: string;
|
||||
// Token to access of the data.
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export interface RESTModel {
|
||||
// base of the local API request: "sheep/{id}".
|
||||
endPoint: string;
|
||||
// Type of the request.
|
||||
requestType?: HTTPRequestModel;
|
||||
// Input type requested.
|
||||
accept?: HTTPMimeType;
|
||||
// Content of the local data.
|
||||
contentType?: HTTPMimeType;
|
||||
// Mode of the TOKEN in URL or Header (?token:${tokenInUrl})
|
||||
tokenInUrl?: boolean;
|
||||
}
|
||||
|
||||
export interface ModelResponseHttp {
|
||||
status: number;
|
||||
data: any;
|
||||
}
|
||||
|
||||
function isNullOrUndefined(data: any): data is undefined | null {
|
||||
return data === undefined || data === null;
|
||||
}
|
||||
|
||||
// generic progression callback
|
||||
export type ProgressCallback = (count: number, total: number) => void;
|
||||
|
||||
export interface RESTAbort {
|
||||
abort?: () => boolean;
|
||||
}
|
||||
|
||||
// Rest generic callback have a basic model to upload and download advancement.
|
||||
export interface RESTCallbacks {
|
||||
progressUpload?: ProgressCallback;
|
||||
progressDownload?: ProgressCallback;
|
||||
abortHandle?: RESTAbort;
|
||||
}
|
||||
|
||||
export interface RESTRequestType {
|
||||
restModel: RESTModel;
|
||||
restConfig: RESTConfig;
|
||||
data?: any;
|
||||
params?: object;
|
||||
queries?: object;
|
||||
callbacks?: RESTCallbacks;
|
||||
}
|
||||
|
||||
function replaceAll(input, searchValue, replaceValue) {
|
||||
return input.split(searchValue).join(replaceValue);
|
||||
}
|
||||
|
||||
function removeTrailingSlashes(input: string): string {
|
||||
if (isNullOrUndefined(input)) {
|
||||
return "undefined";
|
||||
}
|
||||
return input.replace(/\/+$/, "");
|
||||
}
|
||||
function removeLeadingSlashes(input: string): string {
|
||||
if (isNullOrUndefined(input)) {
|
||||
return "";
|
||||
}
|
||||
return input.replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
export function RESTUrl({
|
||||
restModel,
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
}: RESTRequestType): string {
|
||||
// Create the URL PATH:
|
||||
let generateUrl = `${removeTrailingSlashes(
|
||||
restConfig.server
|
||||
)}/${removeLeadingSlashes(restModel.endPoint)}`;
|
||||
if (params !== undefined) {
|
||||
for (let key of Object.keys(params)) {
|
||||
generateUrl = replaceAll(generateUrl, `{${key}}`, `${params[key]}`);
|
||||
}
|
||||
}
|
||||
if (
|
||||
queries === undefined &&
|
||||
(restConfig.token === undefined || restModel.tokenInUrl !== true)
|
||||
) {
|
||||
return generateUrl;
|
||||
}
|
||||
const searchParams = new URLSearchParams();
|
||||
if (queries !== undefined) {
|
||||
for (let key of Object.keys(queries)) {
|
||||
const value = queries[key];
|
||||
if (Array.isArray(value)) {
|
||||
for (const element of value) {
|
||||
searchParams.append(`${key}`, `${element}`);
|
||||
}
|
||||
} else {
|
||||
searchParams.append(`${key}`, `${value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
|
||||
searchParams.append("Authorization", `Bearer ${restConfig.token}`);
|
||||
}
|
||||
return generateUrl + "?" + searchParams.toString();
|
||||
}
|
||||
|
||||
export function fetchProgress(
|
||||
generateUrl: string,
|
||||
{
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
}: {
|
||||
method: HTTPRequestModel;
|
||||
headers: any;
|
||||
body: any;
|
||||
},
|
||||
{ progressUpload, progressDownload, abortHandle }: RESTCallbacks
|
||||
): Promise<Response> {
|
||||
const xhr: {
|
||||
io?: XMLHttpRequest;
|
||||
} = {
|
||||
io: new XMLHttpRequest(),
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
// Stream the upload progress
|
||||
if (progressUpload) {
|
||||
xhr.io?.upload.addEventListener("progress", (dataEvent) => {
|
||||
if (dataEvent.lengthComputable) {
|
||||
progressUpload(dataEvent.loaded, dataEvent.total);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Stream the download progress
|
||||
if (progressDownload) {
|
||||
xhr.io?.addEventListener("progress", (dataEvent) => {
|
||||
if (dataEvent.lengthComputable) {
|
||||
progressDownload(dataEvent.loaded, dataEvent.total);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (abortHandle) {
|
||||
abortHandle.abort = () => {
|
||||
if (xhr.io) {
|
||||
console.log(`Request abort on the XMLHttpRequest: ${generateUrl}`);
|
||||
xhr.io.abort();
|
||||
return true;
|
||||
}
|
||||
console.log(
|
||||
`Request abort (FAIL) on the XMLHttpRequest: ${generateUrl}`
|
||||
);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
// Check if we have an internal Fail:
|
||||
xhr.io?.addEventListener("error", () => {
|
||||
xhr.io = undefined;
|
||||
reject(new TypeError("Failed to fetch"));
|
||||
});
|
||||
|
||||
// Capture the end of the stream
|
||||
xhr.io?.addEventListener("loadend", () => {
|
||||
if (xhr.io?.readyState !== XMLHttpRequest.DONE) {
|
||||
return;
|
||||
}
|
||||
if (xhr.io?.status === 0) {
|
||||
//the stream has been aborted
|
||||
reject(new TypeError("Fetch has been aborted"));
|
||||
return;
|
||||
}
|
||||
// Stream is ended, transform in a generic response:
|
||||
const response = new Response(xhr.io.response, {
|
||||
status: xhr.io.status,
|
||||
statusText: xhr.io.statusText,
|
||||
});
|
||||
const headersArray = replaceAll(
|
||||
xhr.io.getAllResponseHeaders().trim(),
|
||||
"\r\n",
|
||||
"\n"
|
||||
).split("\n");
|
||||
headersArray.forEach(function (header) {
|
||||
const firstColonIndex = header.indexOf(":");
|
||||
if (firstColonIndex !== -1) {
|
||||
const key = header.substring(0, firstColonIndex).trim();
|
||||
const value = header.substring(firstColonIndex + 1).trim();
|
||||
response.headers.set(key, value);
|
||||
} else {
|
||||
response.headers.set(header, "");
|
||||
}
|
||||
});
|
||||
xhr.io = undefined;
|
||||
resolve(response);
|
||||
});
|
||||
xhr.io?.open(method, generateUrl, true);
|
||||
if (!isNullOrUndefined(headers)) {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
xhr.io?.setRequestHeader(key, value as string);
|
||||
}
|
||||
}
|
||||
xhr.io?.send(body);
|
||||
});
|
||||
}
|
||||
|
||||
export function RESTRequest({
|
||||
restModel,
|
||||
restConfig,
|
||||
data,
|
||||
params,
|
||||
queries,
|
||||
callbacks,
|
||||
}: RESTRequestType): Promise<ModelResponseHttp> {
|
||||
// Create the URL PATH:
|
||||
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
|
||||
let headers: any = {};
|
||||
if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
|
||||
headers["Authorization"] = `Bearer ${restConfig.token}`;
|
||||
}
|
||||
if (restModel.accept !== undefined) {
|
||||
headers["Accept"] = restModel.accept;
|
||||
}
|
||||
if (restModel.requestType !== HTTPRequestModel.GET) {
|
||||
// if Get we have not a content type, the body is empty
|
||||
if (restModel.contentType !== HTTPMimeType.MULTIPART) {
|
||||
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
|
||||
headers["Content-Type"] = restModel.contentType;
|
||||
}
|
||||
}
|
||||
let body = data;
|
||||
if (restModel.contentType === HTTPMimeType.JSON) {
|
||||
body = JSON.stringify(data);
|
||||
} else if (restModel.contentType === HTTPMimeType.MULTIPART) {
|
||||
const formData = new FormData();
|
||||
for (const name in data) {
|
||||
formData.append(name, data[name]);
|
||||
}
|
||||
body = formData;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let action: undefined | Promise<Response> = undefined;
|
||||
if (
|
||||
isNullOrUndefined(callbacks) ||
|
||||
(isNullOrUndefined(callbacks.progressDownload) &&
|
||||
isNullOrUndefined(callbacks.progressUpload) &&
|
||||
isNullOrUndefined(callbacks.abortHandle))
|
||||
) {
|
||||
// No information needed: call the generic fetch interface
|
||||
action = fetch(generateUrl, {
|
||||
method: restModel.requestType,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
// need progression information: call old fetch model (XMLHttpRequest) that permit to keep % upload and % download for HTTP1.x
|
||||
action = fetchProgress(
|
||||
generateUrl,
|
||||
{
|
||||
method: restModel.requestType ?? HTTPRequestModel.GET,
|
||||
headers,
|
||||
body,
|
||||
},
|
||||
callbacks
|
||||
);
|
||||
}
|
||||
action
|
||||
.then((response: Response) => {
|
||||
if (response.status >= 200 && response.status <= 299) {
|
||||
const contentType = response.headers.get("Content-Type");
|
||||
if (
|
||||
!isNullOrUndefined(restModel.accept) &&
|
||||
restModel.accept !== contentType
|
||||
) {
|
||||
reject({
|
||||
name: "Model accept type incompatible",
|
||||
time: Date().toString(),
|
||||
status: 901,
|
||||
message: `REST Content type are not compatible: ${restModel.accept} != ${contentType}`,
|
||||
statusMessage: "Fetch error",
|
||||
error: "rest-tools.ts Wrong type in the message return type",
|
||||
} as RestErrorResponse);
|
||||
} else if (contentType === HTTPMimeType.JSON) {
|
||||
response
|
||||
.json()
|
||||
.then((value: any) => {
|
||||
resolve({ status: response.status, data: value });
|
||||
})
|
||||
.catch((reason: Error) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
time: Date().toString(),
|
||||
status: 902,
|
||||
message: `REST parse json fail: ${reason}`,
|
||||
statusMessage: "Fetch parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
} as RestErrorResponse);
|
||||
});
|
||||
} else {
|
||||
resolve({ status: response.status, data: response.body });
|
||||
}
|
||||
} else {
|
||||
// the answer is not correct not a 2XX
|
||||
// clone the response to keep the raw data if case of error:
|
||||
response
|
||||
.clone()
|
||||
.json()
|
||||
.then((value: any) => {
|
||||
if (isRestErrorResponse(value)) {
|
||||
reject(value);
|
||||
} else {
|
||||
response
|
||||
.text()
|
||||
.then((dataError: string) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
time: Date().toString(),
|
||||
status: 903,
|
||||
message: `REST parse error json with wrong type fail. ${dataError}`,
|
||||
statusMessage: "Fetch parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
} as RestErrorResponse);
|
||||
})
|
||||
.catch((reason: any) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
time: Date().toString(),
|
||||
status: response.status,
|
||||
message: `unmanaged error model: ??? with error: ${reason}`,
|
||||
statusMessage: "Fetch ERROR parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
} as RestErrorResponse);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((reason: Error) => {
|
||||
response
|
||||
.text()
|
||||
.then((dataError: string) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
time: Date().toString(),
|
||||
status: response.status,
|
||||
message: `unmanaged error model: ${dataError} with error: ${reason}`,
|
||||
statusMessage: "Fetch ERROR TEXT parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
} as RestErrorResponse);
|
||||
})
|
||||
.catch((reason: any) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
time: Date().toString(),
|
||||
status: response.status,
|
||||
message: `unmanaged error model: ??? with error: ${reason}`,
|
||||
statusMessage: "Fetch ERROR TEXT FAIL",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
} as RestErrorResponse);
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
if (isRestErrorResponse(error)) {
|
||||
reject(error);
|
||||
} else {
|
||||
reject({
|
||||
name: "Request fail",
|
||||
time: Date(),
|
||||
status: 999,
|
||||
message: error,
|
||||
statusMessage: "Fetch catch error",
|
||||
error: "rest-tools.ts detect an error in the fetch request",
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function RESTRequestJson<TYPE>(
|
||||
request: RESTRequestType,
|
||||
checker?: (data: any) => data is TYPE
|
||||
): Promise<TYPE> {
|
||||
return new Promise((resolve, reject) => {
|
||||
RESTRequest(request)
|
||||
.then((value: ModelResponseHttp) => {
|
||||
if (isNullOrUndefined(checker)) {
|
||||
console.log(`Have no check of MODEL in API: ${RESTUrl(request)}`);
|
||||
resolve(value.data);
|
||||
} else if (checker === undefined || checker(value.data)) {
|
||||
resolve(value.data);
|
||||
} else {
|
||||
reject({
|
||||
name: "Model check fail",
|
||||
time: Date().toString(),
|
||||
status: 950,
|
||||
error: "REST Fail to verify the data",
|
||||
statusMessage: "API cast ERROR",
|
||||
message: "api.ts Check type as fail",
|
||||
} as RestErrorResponse);
|
||||
}
|
||||
})
|
||||
.catch((reason: RestErrorResponse) => {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function RESTRequestVoid(request: RESTRequestType): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
RESTRequest(request)
|
||||
.then((value: ModelResponseHttp) => {
|
||||
resolve();
|
||||
})
|
||||
.catch((reason: RestErrorResponse) => {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
}
|
35
front2/src/components/Layout/PageLayout.tsx
Normal file
35
front2/src/components/Layout/PageLayout.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React, { ReactNode, useEffect } from 'react';
|
||||
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { TOP_BAR_HEIGHT } from '@/components/TopBar/TopBar';
|
||||
|
||||
export type LayoutProps = React.PropsWithChildren<unknown> & {
|
||||
topBar?: ReactNode;
|
||||
};
|
||||
|
||||
export const PageLayout = ({ children }: LayoutProps) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
overflowX="auto"
|
||||
overflowY="auto"
|
||||
minH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||
maxH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||
position="absolute"
|
||||
top={TOP_BAR_HEIGHT}
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
41
front2/src/components/Layout/PageLayoutInfoCenter.tsx
Normal file
41
front2/src/components/Layout/PageLayoutInfoCenter.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { ReactNode, useEffect } from 'react';
|
||||
|
||||
import { Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { colors } from '@/theme/foundations/colors';
|
||||
|
||||
export type LayoutProps = FlexProps & {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const PageLayoutInfoCenter = ({
|
||||
children,
|
||||
width = '25%',
|
||||
...rest
|
||||
}: LayoutProps) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
<Flex
|
||||
direction="column"
|
||||
margin="auto"
|
||||
minWidth={width}
|
||||
border="back.900"
|
||||
borderWidth="1px"
|
||||
borderRadius="8px"
|
||||
padding="10px"
|
||||
boxShadow={'0px 0px 16px ' + colors.back[900]}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
180
front2/src/components/TopBar/TopBar.tsx
Normal file
180
front2/src/components/TopBar/TopBar.tsx
Normal file
@ -0,0 +1,180 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
Flex,
|
||||
HStack,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
LuAlignJustify,
|
||||
LuArrowBigLeft,
|
||||
LuArrowRightSquare,
|
||||
LuArrowUpSquare,
|
||||
LuHelpCircle,
|
||||
LuHome,
|
||||
LuLogIn,
|
||||
LuLogOut,
|
||||
LuMoon,
|
||||
LuPlusCircle,
|
||||
LuSettings,
|
||||
LuSun,
|
||||
LuUserCircle,
|
||||
} from 'react-icons/lu';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import { colors } from '@/theme/foundations/colors';
|
||||
import { requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
||||
import { useThemeMode } from '@/utils/theme-tools';
|
||||
|
||||
export const TOP_BAR_HEIGHT = '50px';
|
||||
|
||||
export type TopBarProps = {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const TopBar = ({ children }: TopBarProps) => {
|
||||
const { mode, colorMode, toggleColorMode } = useThemeMode();
|
||||
const buttonProperty = {
|
||||
variant: '@menu',
|
||||
height: TOP_BAR_HEIGHT,
|
||||
};
|
||||
const { session } = useServiceContext();
|
||||
const backColor = mode('back.100', 'back.800');
|
||||
const drawerDisclose = useDisclosure();
|
||||
const onChangeTheme = () => {
|
||||
drawerDisclose.onOpen();
|
||||
};
|
||||
const navigate = useNavigate();
|
||||
const onSignIn = (): void => {
|
||||
requestSignIn();
|
||||
};
|
||||
const onSignUp = (): void => {
|
||||
requestSignUp();
|
||||
};
|
||||
const onSignOut = (): void => {
|
||||
requestSignOut();
|
||||
};
|
||||
const onSelectHome = () => {
|
||||
navigate('/');
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
height={TOP_BAR_HEIGHT}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
backgroundColor={backColor}
|
||||
gap="2"
|
||||
px="2"
|
||||
boxShadow={'0px 2px 4px ' + colors.back[900]}
|
||||
zIndex={200}
|
||||
>
|
||||
<Button {...buttonProperty} onClick={onChangeTheme} marginRight="auto">
|
||||
<LuAlignJustify />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Menu
|
||||
</Text>
|
||||
</Button>
|
||||
{children}
|
||||
{session?.state !== SessionState.CONNECTED && (
|
||||
<>
|
||||
<Button {...buttonProperty} onClick={onSignIn}>
|
||||
<LuLogIn />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Sign-in
|
||||
</Text>
|
||||
</Button>
|
||||
<Button {...buttonProperty} onClick={onSignUp} disabled={true}>
|
||||
<LuPlusCircle />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Sign-up
|
||||
</Text>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{session?.state === SessionState.CONNECTED && (
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label="Options"
|
||||
icon={<LuUserCircle />}
|
||||
{...buttonProperty}
|
||||
width={TOP_BAR_HEIGHT}
|
||||
/>
|
||||
<MenuList>
|
||||
<MenuItem _hover={{}} color={mode('brand.800', 'brand.200')}>
|
||||
Sign in as {session?.login ?? 'Fail'}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<LuArrowUpSquare />}>Add Media</MenuItem>
|
||||
<MenuItem icon={<LuSettings />}>Settings</MenuItem>
|
||||
<MenuItem icon={<LuHelpCircle />}>Help</MenuItem>
|
||||
<MenuItem icon={<LuLogOut onClick={onSignOut} />}>
|
||||
Sign-out
|
||||
</MenuItem>
|
||||
{colorMode === 'light' ? (
|
||||
<MenuItem icon={<LuMoon />} onClick={toggleColorMode}>
|
||||
Set dark mode
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem icon={<LuSun />} onClick={toggleColorMode}>
|
||||
Set light mode
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
)}
|
||||
<Drawer
|
||||
placement="left"
|
||||
onClose={drawerDisclose.onClose}
|
||||
isOpen={drawerDisclose.isOpen}
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerHeader
|
||||
paddingY="auto"
|
||||
onClick={drawerDisclose.onClose}
|
||||
boxShadow={'0px 2px 4px ' + colors.back[900]}
|
||||
backgroundColor={backColor}
|
||||
color={mode('brand.900', 'brand.50')}
|
||||
textTransform="uppercase"
|
||||
>
|
||||
<HStack height={TOP_BAR_HEIGHT}>
|
||||
<LuArrowBigLeft />
|
||||
<Text as="span" paddingLeft="3px">
|
||||
Karusic
|
||||
</Text>
|
||||
</HStack>
|
||||
</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<Button {...buttonProperty} onClick={onSelectHome} width="fill">
|
||||
<LuHome />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Home
|
||||
</Text>
|
||||
</Button>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</Flex>
|
||||
);
|
||||
};
|
3
front2/src/components/index.ts
Normal file
3
front2/src/components/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
export * from './Icons';
|
||||
|
28
front2/src/config/dayjs.ts
Normal file
28
front2/src/config/dayjs.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/fr';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||
import dayOfYear from 'dayjs/plugin/dayOfYear';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import isBetween from 'dayjs/plugin/isBetween';
|
||||
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
||||
import isToday from 'dayjs/plugin/isToday';
|
||||
import isTomorrow from 'dayjs/plugin/isTomorrow';
|
||||
import isYesterday from 'dayjs/plugin/isYesterday';
|
||||
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||
|
||||
dayjs.locale('fr');
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(weekOfYear);
|
||||
dayjs.extend(isSameOrAfter);
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isTomorrow);
|
||||
dayjs.extend(isYesterday);
|
||||
dayjs.extend(dayOfYear);
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(quarterOfYear);
|
||||
dayjs.extend(duration);
|
2
front2/src/config/index.ts
Normal file
2
front2/src/config/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import './axios';
|
||||
import './dayjs';
|
2
front2/src/constants/date.ts
Normal file
2
front2/src/constants/date.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const DATE_FORMAT = 'YYYY-MM-DD';
|
||||
export const DATE_FORMAT_FULL = 'dddd DD MMMM HH:mm';
|
1
front2/src/constants/index.ts
Normal file
1
front2/src/constants/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './date'
|
107
front2/src/environment.ts
Normal file
107
front2/src/environment.ts
Normal file
@ -0,0 +1,107 @@
|
||||
export interface Environment {
|
||||
production: boolean;
|
||||
applName: string;
|
||||
defaultServer: string;
|
||||
server: {
|
||||
[key: string]: string;
|
||||
};
|
||||
ssoSite: string;
|
||||
ssoSignIn: string;
|
||||
ssoSignUp: string;
|
||||
ssoSignOut: string;
|
||||
tokenStoredInPermanentStorage: boolean;
|
||||
replaceDataToRealServer?: boolean;
|
||||
}
|
||||
|
||||
const serverSSOAddress = 'http://atria-soft.org';
|
||||
|
||||
const environment_back_prod: Environment = {
|
||||
production: false,
|
||||
// URL of development API
|
||||
applName: 'karusic',
|
||||
defaultServer: 'karusic',
|
||||
server: {
|
||||
karusic: `${serverSSOAddress}/karusic/api`,
|
||||
karso: `${serverSSOAddress}/karso/api`,
|
||||
},
|
||||
ssoSite: `${serverSSOAddress}/karso/`,
|
||||
ssoSignIn: `${serverSSOAddress}/karso/signin/karusic-dev/`,
|
||||
ssoSignUp: `${serverSSOAddress}/karso/signup/karusic-dev/`,
|
||||
ssoSignOut: `${serverSSOAddress}/karso/signout/karusic-dev/`,
|
||||
tokenStoredInPermanentStorage: false,
|
||||
};
|
||||
|
||||
const environment_local: Environment = {
|
||||
production: false,
|
||||
// URL of development API
|
||||
applName: 'karusic',
|
||||
defaultServer: 'karusic',
|
||||
server: {
|
||||
karusic: 'http://localhost:19080/karusic/api',
|
||||
karso: `${serverSSOAddress}/karso/api`,
|
||||
},
|
||||
ssoSite: `${serverSSOAddress}/karso/`,
|
||||
ssoSignIn: `${serverSSOAddress}/karso/signin/karusic-dev/`,
|
||||
ssoSignUp: `${serverSSOAddress}/karso/signup/karusic-dev/`,
|
||||
ssoSignOut: `${serverSSOAddress}/karso/signout/karusic-dev/`,
|
||||
tokenStoredInPermanentStorage: false,
|
||||
replaceDataToRealServer: true,
|
||||
};
|
||||
|
||||
const environment_full_local: Environment = {
|
||||
production: false,
|
||||
// URL of development API
|
||||
applName: 'karusic',
|
||||
defaultServer: 'karusic',
|
||||
server: {
|
||||
karusic: 'http://localhost:19080/karusic/api',
|
||||
karso: 'http://localhost:15080/karso/api',
|
||||
},
|
||||
ssoSite: `${serverSSOAddress}/karso/`,
|
||||
ssoSignIn: 'http://localhost:4200/signin/karusic-dev/',
|
||||
ssoSignUp: 'http://localhost:4200/signup/karusic-dev/',
|
||||
ssoSignOut: 'http://localhost:4200/signout/karusic-dev/',
|
||||
tokenStoredInPermanentStorage: false,
|
||||
};
|
||||
|
||||
const environment_hybrid: Environment = {
|
||||
production: false,
|
||||
// URL of development API
|
||||
applName: 'karusic',
|
||||
defaultServer: 'karusic',
|
||||
server: {
|
||||
karusic: `${serverSSOAddress}/karusic/api`,
|
||||
karso: `${serverSSOAddress}/karso/api`,
|
||||
},
|
||||
ssoSite: `${serverSSOAddress}/karso/`,
|
||||
ssoSignIn: 'http://localhost:4200/signin/karusic-dev/',
|
||||
ssoSignUp: 'http://localhost:4200/signup/karusic-dev/',
|
||||
ssoSignOut: 'http://localhost:4200/signout/karusic-dev/',
|
||||
tokenStoredInPermanentStorage: false,
|
||||
};
|
||||
|
||||
export const environment = environment_local;
|
||||
|
||||
/**
|
||||
* Check if the current environment is for development
|
||||
* @returns true if development is active.
|
||||
*/
|
||||
export const isDevelopmentEnvironment = () => {
|
||||
return import.meta.env.MODE === 'development';
|
||||
};
|
||||
|
||||
/**
|
||||
* get the current REST api URL. Depend on the VITE_API_BASE_URL env variable.
|
||||
* @returns The URL with http(s)://***
|
||||
*/
|
||||
export const getApiUrl = () => {
|
||||
const baseUrl: string | undefined = import.meta.env.VITE_API_BASE_URL;
|
||||
if (baseUrl === undefined || baseUrl === null) {
|
||||
//return `${window.location.protocol}//${window.location.host}/api`;
|
||||
return environment.server.karusic;
|
||||
}
|
||||
if (baseUrl.startsWith('http')) {
|
||||
return baseUrl;
|
||||
}
|
||||
return `${window.location.protocol}//${window.location.host}/${baseUrl}`;
|
||||
};
|
133
front2/src/errors/Error404.tsx
Normal file
133
front2/src/errors/Error404.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Heading,
|
||||
Stack,
|
||||
Text,
|
||||
useTheme,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { environment } from '@/environment';
|
||||
|
||||
const Illustration = ({ colorScheme = 'gray', ...rest }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const color = theme?.colors?.[colorScheme] ?? {};
|
||||
return (
|
||||
<Box
|
||||
as="svg"
|
||||
width={400}
|
||||
height={300}
|
||||
maxW="full"
|
||||
viewBox="0 0 400 300"
|
||||
fill="none"
|
||||
{...rest}
|
||||
>
|
||||
<path
|
||||
// Left Hand
|
||||
d="M65.013 104.416s-12.773-.562-13.719 11.938c-.946 12.5 16.13 8.397 13.719-11.938z"
|
||||
fill={color['300']}
|
||||
/>
|
||||
<path
|
||||
// Left Arm
|
||||
d="M182.326 67.705s-35.463-20.529-67.804-13.535c-32.342 6.993-60.624 52.94-60.624 52.94l11.499 6.837s49.74-51.775 83.275-21.444c33.535 30.331 33.654-24.798 33.654-24.798z"
|
||||
fill={color['800']}
|
||||
/>
|
||||
<path
|
||||
// Search Zone
|
||||
d="M334.098 220.092a14.333 14.333 0 01-9.838-7.37v-.106l-50.465-96.17-27.774 19.677 19.642 61.796a10.575 10.575 0 01-5.617 12.799 10.563 10.563 0 01-4.945.97 1037.507 1037.507 0 00-47.278-1.067c-85.178 0-154.23 9.93-154.23 22.184 0 12.255 69.052 22.195 154.23 22.195C293.001 255 362 245.07 362 232.837c0-4.756-10.328-9.14-27.902-12.745z"
|
||||
fill={color['200']}
|
||||
/>
|
||||
<path
|
||||
// Foots
|
||||
d="M173.611 225.563s1.578 5.333 6.256 5.962c4.679.63 5.66 5.333 1.365 6.293-4.296.96-14.921-5.066-14.921-5.066l.671-6.773 6.629-.416zM82.518 224.657s-5.414 1.173-6.395 5.791c-.98 4.618-5.734 5.237-6.395.875-.66-4.362 6.193-14.484 6.193-14.484l6.693 1.205-.096 6.613z"
|
||||
fill={color['900']}
|
||||
/>
|
||||
<path
|
||||
// Left Leg
|
||||
d="M83.5 143s-5.245 25.322 12.713 35.305c17.959 9.983 74.606-7.988 65.856 48.592h12.64s16.338-46.928-26.048-63.609l-12.864-14.601L83.5 143z"
|
||||
fill={color['600']}
|
||||
/>
|
||||
<path
|
||||
// Magnifying Glass Shadow
|
||||
d="M257.632 128.216l-4.299-4.112-26.891 28.16 4.299 4.111 26.891-28.159z"
|
||||
fill={color['700']}
|
||||
/>
|
||||
<path
|
||||
// Magnifying Glass Handle
|
||||
d="M255.537 126.2l-4.299-4.112-26.891 28.16 4.299 4.111 26.891-28.159z"
|
||||
fill={color['500']}
|
||||
/>
|
||||
<path
|
||||
// Magnifying Glass Shadow 2
|
||||
d="M267.233 131.381c6.913-7.239 8.606-16.849 3.78-21.464-4.826-4.615-14.342-2.487-21.256 4.752-6.913 7.24-8.605 16.85-3.779 21.465 4.825 4.615 14.342 2.487 21.255-4.753z"
|
||||
fill={color['700']}
|
||||
/>
|
||||
<path
|
||||
// Magnifying Glass Ring
|
||||
d="M265.133 129.382c6.914-7.24 8.606-16.849 3.78-21.464-4.825-4.615-14.342-2.487-21.255 4.752-6.913 7.24-8.606 16.849-3.78 21.464 4.826 4.615 14.342 2.487 21.255-4.752z"
|
||||
fill={color['500']}
|
||||
/>
|
||||
<path
|
||||
// Magnifying Glass
|
||||
d="M262.167 126.545c4.566-4.782 5.685-11.13 2.497-14.178-3.187-3.048-9.473-1.642-14.04 3.14-4.567 4.782-5.685 11.13-2.498 14.178 3.188 3.048 9.474 1.642 14.041-3.14z"
|
||||
fill={color['50']}
|
||||
/>
|
||||
<path
|
||||
// Head
|
||||
d="M217.261 74.257c1.932-2.325 3.256-4.248 3.256-4.248 3.133-4.106-.267-11.743-6.096-12.084a7.606 7.606 0 00-7.759 4.095l-.966 2.572-19.039 7.678 2.664 15.443 14.063-11.483c.418 1.245 1.052 2.35 1.7 3.26a4.269 4.269 0 004.448 1.638 4.267 4.267 0 001.51-.7 25.197 25.197 0 002.341-1.98l1.613.893a1.364 1.364 0 002.014-1.067l.256-4.02-.005.003z"
|
||||
fill={color['300']}
|
||||
/>
|
||||
<path
|
||||
// Body
|
||||
d="M192.199 93.056l-1.791-10.42a25.45 25.45 0 00-15.251-19.325 45.122 45.122 0 00-10.754-2.827c-4.732-.66-11.361 0-18.779 1.952-31.953 8.394-55.4 35.484-60.25 68.141L83 145l52.797 3.687s-2.664-14.931 15.987-14.931 42.269.192 40.415-40.7z"
|
||||
fill={color['700']}
|
||||
/>
|
||||
<path
|
||||
// Right Arm
|
||||
d="M169.945 95.488c4.977-16.617-14.324-30.055-28.084-19.496-11.51 8.82-24.513 24.701-25.024 51.503-.885 48.368 45.413 39.718 108.071 21.192l-1.993-14.089s-75.032 14.196-64.843-10.207c4.263-10.206 9.23-20.061 11.873-28.903z"
|
||||
fill={color['800']}
|
||||
/>
|
||||
<path
|
||||
// Right Leg
|
||||
d="M143.609 163.406s1.545 53.487-60.995 63.491l-2.42-11.338s50.092-12.425 17.735-45.712l45.68-6.441z"
|
||||
fill={color['600']}
|
||||
/>
|
||||
307s17
|
||||
<path
|
||||
// Right Hand
|
||||
d="M223.298 137.307s17.244-1.824 18.758 4.917c1.513 6.741-1.791 10.313-17.309 5.333l-1.449-10.25z"
|
||||
fill={color['300']}
|
||||
/>
|
||||
<path
|
||||
// Hair
|
||||
d="M218.673 68.942a34.11 34.11 0 01-5.649-5.44 7.094 7.094 0 01-4.934 5.994 5.752 5.752 0 01-6.821-2.655l7.034-8.319a8.644 8.644 0 018.27-3.2c1.33.23 2.643.543 3.933.939 3.197 1.066 5.425 5.567 8.942 5.717a2.044 2.044 0 011.946 2.1c-.01.354-.111.7-.294 1.003-1.727 2.87-5.499 6.517-10.114 5.056a7.933 7.933 0 01-2.313-1.195z"
|
||||
fill={color['800']}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const Error404 = () => {
|
||||
return (
|
||||
<Center flex="1" p="8">
|
||||
<Stack
|
||||
direction={{ base: 'column', md: 'row' }}
|
||||
align="center"
|
||||
spacing="0"
|
||||
>
|
||||
<Illustration />
|
||||
<Box textAlign={{ base: 'center', md: 'left' }}>
|
||||
<Heading>Erreur 404</Heading>
|
||||
<Text color="gray.600">
|
||||
Cette page n'existe plus ou l'URL a changé
|
||||
</Text>
|
||||
<Button as="a" variant="link" href={`/${environment.applName}`}>
|
||||
Retour à l'accueil
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Center>
|
||||
);
|
||||
};
|
51
front2/src/errors/ErrorBoundary.tsx
Normal file
51
front2/src/errors/ErrorBoundary.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertIcon,
|
||||
AlertTitle,
|
||||
Box,
|
||||
Button,
|
||||
Collapse,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
FallbackProps,
|
||||
ErrorBoundary as ReactErrorBoundary,
|
||||
} from 'react-error-boundary';
|
||||
import { LuChevronDown, LuChevronUp } from 'react-icons/lu';
|
||||
|
||||
const ErrorFallback = ({ error }: FallbackProps) => {
|
||||
const { isOpen, onToggle } = useDisclosure();
|
||||
return (
|
||||
<Box p="4" m="auto">
|
||||
<Alert status="error" borderRadius="md">
|
||||
<AlertIcon />
|
||||
<Box flex="1">
|
||||
<AlertTitle>An unexpected error has occurred.</AlertTitle>
|
||||
<AlertDescription display="block" lineHeight="1.4">
|
||||
<Button
|
||||
variant="link"
|
||||
color="red.800"
|
||||
size="sm"
|
||||
rightIcon={isOpen ? <LuChevronUp /> : <LuChevronDown />}
|
||||
onClick={onToggle}
|
||||
>
|
||||
Show details
|
||||
</Button>
|
||||
<Collapse in={isOpen} animateOpacity>
|
||||
<Box mt={4} fontFamily="monospace">
|
||||
{error.message}
|
||||
</Box>
|
||||
</Collapse>
|
||||
</AlertDescription>
|
||||
</Box>
|
||||
</Alert>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const ErrorBoundary: FC<React.PropsWithChildren<unknown>> = (props) => {
|
||||
return <ReactErrorBoundary FallbackComponent={ErrorFallback} {...props} />;
|
||||
};
|
1
front2/src/errors/index.ts
Normal file
1
front2/src/errors/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './Error404';
|
14
front2/src/icons/DoubleArrowIcon.tsx
Normal file
14
front2/src/icons/DoubleArrowIcon.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { createIcon } from '@chakra-ui/react';
|
||||
|
||||
export const DoubleArrowIcon = createIcon({
|
||||
displayName: 'DoubleArrowIcon',
|
||||
viewBox: '0 0 24 24',
|
||||
path: (
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M1.293 12.207a1 1 0 0 1 0-1.414l6.364-6.364A1 1 0 0 1 9.07 5.843L4.414 10.5h15.172l-4.657-4.657a1 1 0 0 1 1.414-1.414l6.364 6.364a1 1 0 0 1 0 1.414l-6.364 6.364a1 1 0 0 1-1.414-1.414l4.657-4.657H4.414l4.657 4.657a1 1 0 1 1-1.414 1.414l-6.364-6.364Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
),
|
||||
});
|
1
front2/src/icons/index.ts
Normal file
1
front2/src/icons/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './DoubleArrowIcon';
|
16
front2/src/main.tsx
Normal file
16
front2/src/main.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { StrictMode } from 'react';
|
||||
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from '@/App';
|
||||
|
||||
// Render the app
|
||||
const rootElement = document.getElementById('root');
|
||||
if (rootElement && !rootElement.innerHTML) {
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
33
front2/src/scene/App.tsx
Normal file
33
front2/src/scene/App.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { createBrowserHistory } from 'history';
|
||||
import {
|
||||
unstable_HistoryRouter as HistoryRouter,
|
||||
Route,
|
||||
Routes,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { Error404 } from '@/errors';
|
||||
import { ErrorBoundary } from '@/errors/ErrorBoundary';
|
||||
import { ServiceContextProvider } from '@/service/ServiceContext';
|
||||
import { ArtistRoutes } from '@/scene/artist/ArtistRoutes';
|
||||
import { HomePage } from '@/scene/home/HomePage';
|
||||
import { SSORoutes } from '@/scene/sso/SSORoutes';
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
<ServiceContextProvider>
|
||||
<ErrorBoundary>
|
||||
<HistoryRouter
|
||||
history={createBrowserHistory({ window })}
|
||||
basename="/karusic"
|
||||
>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="artist/*" element={<ArtistRoutes />} />
|
||||
<Route path="sso/*" element={<SSORoutes />} />
|
||||
<Route path="*" element={<Error404 />} />
|
||||
</Routes>
|
||||
</HistoryRouter>
|
||||
</ErrorBoundary>
|
||||
</ServiceContextProvider>
|
||||
);
|
||||
};
|
15
front2/src/scene/artist/ArtistRoutes.tsx
Normal file
15
front2/src/scene/artist/ArtistRoutes.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Text } from '@chakra-ui/react';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
|
||||
export const ArtistRoutes = () => {
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<PageLayout>
|
||||
<Text>artist Page</Text>;
|
||||
</PageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
105
front2/src/scene/home/HomePage.tsx
Normal file
105
front2/src/scene/home/HomePage.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import { Text } from '@chakra-ui/react';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
|
||||
export const HomePage = () => {
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<PageLayout>
|
||||
<Text>Home Page 1</Text>
|
||||
<br />
|
||||
<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>
|
||||
Home Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home
|
||||
Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home Page
|
||||
3Home Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home Page
|
||||
3Home Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home Page
|
||||
3Home Page 3Home Page 3Home Page 3Home Page 3Home Page 3Home Page
|
||||
3Home Page 3Home Page 3
|
||||
</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;<Text>Home Page 2</Text>
|
||||
<br />
|
||||
<Text>Home Page 3</Text>
|
||||
<br />
|
||||
<Text>Home Page 4</Text>
|
||||
<br />
|
||||
<Text>Home Page 5</Text>
|
||||
<br />
|
||||
<Text>Home Page 6</Text>
|
||||
<br />;
|
||||
</PageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
15
front2/src/scene/sso/SSOEmpty.tsx
Normal file
15
front2/src/scene/sso/SSOEmpty.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Text } from '@chakra-ui/react';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
|
||||
export const SSOEmpty = () => {
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<PageLayout>
|
||||
<Text>SSO empty page</Text>;
|
||||
</PageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
85
front2/src/scene/sso/SSOPage.tsx
Normal file
85
front2/src/scene/sso/SSOPage.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { Center, Heading, Image, Text } from '@chakra-ui/react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import avatar_generic from '@/assets/images/avatar_generic.svg';
|
||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
import { isDevelopmentEnvironment } from '@/environment';
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import { useSessionService } from '@/service/session';
|
||||
import { b64_to_utf8 } from '@/utils/sso';
|
||||
|
||||
export const SSOPage = () => {
|
||||
const { data, keepConnected, token } = useParams();
|
||||
console.log(`- data: ${data}`);
|
||||
console.log(`- keepConnected: ${keepConnected}`);
|
||||
console.log(`- token: ${token}`);
|
||||
const navigate = useNavigate();
|
||||
const { state, setToken, login, clearToken } = useSessionService();
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
setToken(token);
|
||||
} else {
|
||||
clearToken();
|
||||
}
|
||||
}, [token, setToken, clearToken]);
|
||||
const delay = isDevelopmentEnvironment() ? 20000 : 2000;
|
||||
useEffect(() => {
|
||||
if (state === SessionState.CONNECTED) {
|
||||
const destination = data ? b64_to_utf8(data) : '/';
|
||||
console.log(`program redirect to: ${destination} (${delay}ms)`);
|
||||
setTimeout(() => {
|
||||
navigate(`/${destination}`);
|
||||
}, delay);
|
||||
}
|
||||
}, [state]);
|
||||
/*
|
||||
const [searchParams] = useSearchParams();
|
||||
console.log(`data: ${searchParams.get('data')}`);
|
||||
console.log(`keepConnected: ${searchParams.get('keepConnected')}`);
|
||||
console.log(`token: ${searchParams.get('token')}`);
|
||||
const dataFromParam = useGetCreateActionParams();
|
||||
console.log(`data group: ${JSON.stringify(dataFromParam, null, 2)}`);
|
||||
*/
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<PageLayoutInfoCenter width="35%" gap="15px">
|
||||
<Center w="full">
|
||||
<Heading size="xl">LOGIN (after SSO) </Heading>
|
||||
</Center>
|
||||
|
||||
<Center w="full">
|
||||
<Image src={avatar_generic} boxSize="150px" borderRadius="full" />
|
||||
</Center>
|
||||
{token === '__CANCEL__' && (
|
||||
<Text>
|
||||
<b>ERROR: </b> Request cancel of connection !
|
||||
</Text>
|
||||
)}
|
||||
{token === '__FAIL__' && (
|
||||
<Text>
|
||||
<b>ERROR: </b> Connection FAIL !
|
||||
</Text>
|
||||
)}
|
||||
{token === '__LOGOUT__' && (
|
||||
<Text>
|
||||
<b>Dis-connected: </b> Redirect soon!{' '}
|
||||
</Text>
|
||||
)}
|
||||
{!['__LOGOUT__', '__FAIL__', '__CANCEL__'].includes(token ?? '') && (
|
||||
<>
|
||||
<Text>
|
||||
<b>Connected: </b> Redirect soon!
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Welcome back: </b> {login}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</PageLayoutInfoCenter>
|
||||
</>
|
||||
);
|
||||
};
|
14
front2/src/scene/sso/SSORoutes.tsx
Normal file
14
front2/src/scene/sso/SSORoutes.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { ArtistRoutes } from '@/scene/artist/ArtistRoutes';
|
||||
import { SSOEmpty } from '@/scene/sso/SSOEmpty';
|
||||
import { SSOPage } from '@/scene/sso/SSOPage';
|
||||
|
||||
export const SSORoutes = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path=":data/:keepConnected/:token" element={<SSOPage />} />
|
||||
<Route path="*" element={<SSOEmpty />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
44
front2/src/service/ServiceContext.tsx
Normal file
44
front2/src/service/ServiceContext.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { ReactNode, createContext, useContext, useMemo } from 'react';
|
||||
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import {
|
||||
RightPart,
|
||||
SessionServiceProps,
|
||||
useSessionService,
|
||||
useSessionServiceWrapped,
|
||||
} from '@/service/session';
|
||||
|
||||
export type ServiceContextType = {
|
||||
session: SessionServiceProps;
|
||||
};
|
||||
|
||||
export const ServiceContext = createContext<ServiceContextType>({
|
||||
session: {
|
||||
setToken: (token: string) => {},
|
||||
clearToken: () => {},
|
||||
hasReadRight: (part: RightPart) => false,
|
||||
hasWriteRight: (part: RightPart) => false,
|
||||
state: SessionState.NO_USER,
|
||||
},
|
||||
});
|
||||
|
||||
export const useServiceContext = () => useContext(ServiceContext);
|
||||
|
||||
export const ServiceContextProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const session = useSessionServiceWrapped();
|
||||
const contextObjectData = useMemo(
|
||||
() => ({
|
||||
session,
|
||||
}),
|
||||
[session]
|
||||
);
|
||||
return (
|
||||
<ServiceContext.Provider value={contextObjectData}>
|
||||
{children}
|
||||
</ServiceContext.Provider>
|
||||
);
|
||||
};
|
7
front2/src/service/SessionState.ts
Normal file
7
front2/src/service/SessionState.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export enum SessionState {
|
||||
NO_USER,
|
||||
CONNECTING,
|
||||
CONNECTION_FAIL,
|
||||
CONNECTED,
|
||||
DISCONNECT,
|
||||
}
|
125
front2/src/service/session.ts
Normal file
125
front2/src/service/session.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { PartRight, RESTConfig, UserMe, UserResource } from '@/back-api';
|
||||
import { environment, getApiUrl } from '@/environment';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import { isBrowser } from '@/utils/layout';
|
||||
import { parseToken } from '@/utils/sso';
|
||||
|
||||
const TOKEN_KEY = 'karusic-token-key-storage';
|
||||
|
||||
export const USERS = {
|
||||
ADMIN:
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E',
|
||||
NO_USER: '',
|
||||
} as const;
|
||||
|
||||
export const getUserToken = () => {
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
};
|
||||
export type RightPart = 'ADMIN' | 'USER';
|
||||
|
||||
export function getRestConfig(): RESTConfig {
|
||||
return {
|
||||
server: getApiUrl(),
|
||||
token: getUserToken() ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
export type SessionServiceProps = {
|
||||
token?: string;
|
||||
setToken: (token: string) => void;
|
||||
clearToken: () => void;
|
||||
login?: string;
|
||||
hasReadRight: (part: RightPart) => boolean;
|
||||
hasWriteRight: (part: RightPart) => boolean;
|
||||
state: SessionState;
|
||||
};
|
||||
|
||||
export const useSessionService = (): SessionServiceProps => {
|
||||
const { session } = useServiceContext();
|
||||
return session;
|
||||
};
|
||||
export const useSessionServiceWrapped = (): SessionServiceProps => {
|
||||
const [token, setToken] = useState<string | undefined>(
|
||||
isBrowser ? (localStorage.getItem(TOKEN_KEY) ?? undefined) : undefined
|
||||
);
|
||||
const [state, setState] = useState<SessionState>(SessionState.NO_USER);
|
||||
const [config, setConfig] = useState<UserMe | undefined>(undefined);
|
||||
|
||||
const updateRight = useCallback(() => {
|
||||
if (isBrowser) {
|
||||
console.log('Detect a new token...');
|
||||
setState(SessionState.NO_USER);
|
||||
setConfig(undefined);
|
||||
if (token === undefined) {
|
||||
console.log(` ==> No User`);
|
||||
setState(SessionState.NO_USER);
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
} else if (token === '__LOGOUT__') {
|
||||
console.log(` ==> disconnection: ${token}`);
|
||||
setState(SessionState.DISCONNECT);
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
} else if (!['__LOGOUT__', '__FAIL__', '__CANCEL__'].includes(token)) {
|
||||
console.log(' ==> Login ... (try to keep right)');
|
||||
setState(SessionState.CONNECTING);
|
||||
localStorage.setItem(TOKEN_KEY, token);
|
||||
UserResource.getMe({
|
||||
restConfig: getRestConfig(),
|
||||
})
|
||||
.then((response: UserMe) => {
|
||||
console.log(` ==> New right arrived to '${response.login}'`);
|
||||
setState(SessionState.CONNECTED);
|
||||
setConfig(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
setState(SessionState.CONNECTION_FAIL);
|
||||
console.log(` ==> Fail to get right: '${error}'`);
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [localStorage, parseToken, token]);
|
||||
const setTokenLocal = useCallback(
|
||||
(token: string) => {
|
||||
setToken(token);
|
||||
updateRight();
|
||||
},
|
||||
[updateRight, setToken]
|
||||
);
|
||||
const clearToken = useCallback(() => {
|
||||
setToken(undefined);
|
||||
updateRight();
|
||||
}, [updateRight, setToken]);
|
||||
const hasReadRight = useCallback(
|
||||
(part: RightPart) => {
|
||||
const right = config?.rights[environment.applName];
|
||||
if (right === undefined) {
|
||||
return false;
|
||||
}
|
||||
return [PartRight.READ, PartRight.READ_WRITE].includes(right[part]);
|
||||
},
|
||||
[config]
|
||||
);
|
||||
const hasWriteRight = useCallback(
|
||||
(part: RightPart) => {
|
||||
const right = config?.rights[environment.applName];
|
||||
if (right === undefined) {
|
||||
return false;
|
||||
}
|
||||
return [PartRight.READ, PartRight.READ_WRITE].includes(right[part]);
|
||||
},
|
||||
[config]
|
||||
);
|
||||
|
||||
return {
|
||||
token,
|
||||
setToken: setTokenLocal,
|
||||
clearToken,
|
||||
login: config?.login,
|
||||
hasReadRight,
|
||||
hasWriteRight,
|
||||
state,
|
||||
};
|
||||
};
|
65
front2/src/test/setup.ts
Normal file
65
front2/src/test/setup.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import mediaQuery from 'css-mediaquery';
|
||||
|
||||
window.scrollTo = () => undefined;
|
||||
|
||||
global.matchMedia =
|
||||
global.matchMedia ||
|
||||
function (query) {
|
||||
const instance = {
|
||||
matches: mediaQuery.match(query, {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
}),
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
};
|
||||
|
||||
// Listen to resize events from window.resizeTo and update the instance's match
|
||||
window.addEventListener('resize', () => {
|
||||
const change = mediaQuery.match(query, {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
if (change != instance.matches) {
|
||||
instance.matches = change;
|
||||
instance.dispatchEvent('change');
|
||||
}
|
||||
});
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
// Mock window.resizeTo's impl.
|
||||
Object.defineProperty(window, 'resizeTo', {
|
||||
value: (width, height) => {
|
||||
Object.defineProperty(window, 'innerWidth', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: width,
|
||||
});
|
||||
Object.defineProperty(window, 'outerWidth', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: width,
|
||||
});
|
||||
Object.defineProperty(window, 'innerHeight', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: height,
|
||||
});
|
||||
Object.defineProperty(window, 'outerHeight', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: height,
|
||||
});
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
},
|
||||
});
|
27
front2/src/test/utils.tsx
Normal file
27
front2/src/test/utils.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { ReactElement, ReactNode } from 'react';
|
||||
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import { RenderOptions, render } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import theme from '@/theme';
|
||||
|
||||
const CustomWrapper = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<BrowserRouter>{children}</BrowserRouter>
|
||||
</ChakraProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const customRender = (ui: ReactElement, options: RenderOptions = {}) =>
|
||||
render(ui, {
|
||||
wrapper: CustomWrapper,
|
||||
...options,
|
||||
});
|
||||
|
||||
// re-export everything
|
||||
export * from '@testing-library/react';
|
||||
|
||||
// override render method
|
||||
export { customRender as render };
|
24
front2/src/theme/components/badge.ts
Normal file
24
front2/src/theme/components/badge.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
sizes: {
|
||||
'2xs': {
|
||||
fontSize: '0.5em',
|
||||
},
|
||||
xs: {
|
||||
fontSize: '0.6em',
|
||||
},
|
||||
sm: {
|
||||
fontSize: '0.7em',
|
||||
},
|
||||
md: {
|
||||
fontSize: '0.8em',
|
||||
textTransform: 'none',
|
||||
},
|
||||
lg: {
|
||||
fontSize: '0.9em',
|
||||
textTransform: 'none',
|
||||
},
|
||||
},
|
||||
defaultProps: {
|
||||
size: 'md',
|
||||
},
|
||||
};
|
187
front2/src/theme/components/button.ts
Normal file
187
front2/src/theme/components/button.ts
Normal file
@ -0,0 +1,187 @@
|
||||
import { border, defineStyleConfig, keyframes } from '@chakra-ui/react';
|
||||
import { isAccessible, mode, transparentize } from '@chakra-ui/theme-tools';
|
||||
|
||||
import { shadows } from '@/theme/foundations/shadows';
|
||||
|
||||
const shimmer = keyframes`
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
`;
|
||||
|
||||
const customVariant = ({
|
||||
theme,
|
||||
bg,
|
||||
bgHover = bg,
|
||||
bgActive = bgHover,
|
||||
color,
|
||||
colorHover = color,
|
||||
boxColorFocus = '#0x000000',
|
||||
boxShadowHover = 'outline-over',
|
||||
}) => {
|
||||
const isColorAccessible = isAccessible(color, bg, {
|
||||
size: 'large',
|
||||
level: 'AA',
|
||||
})(theme);
|
||||
|
||||
return {
|
||||
bg,
|
||||
color: isColorAccessible ? color : 'black',
|
||||
border: '1px solid #00000000',
|
||||
_focus: {
|
||||
//border: `1px solid ${boxColorFocus}`,
|
||||
border: `1px solid #000000`,
|
||||
},
|
||||
_hover: {
|
||||
bg: bgHover,
|
||||
color: isColorAccessible ? colorHover : 'black',
|
||||
boxShadow: boxShadowHover,
|
||||
_disabled: {
|
||||
bg,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
_active: { bg: bgActive },
|
||||
};
|
||||
};
|
||||
|
||||
export default defineStyleConfig({
|
||||
variants: {
|
||||
// Custom variants
|
||||
'@primary': (props) =>
|
||||
customVariant({
|
||||
theme: props.theme,
|
||||
bg: mode('brand.600', 'brand.300')(props),
|
||||
bgHover: mode('brand.700', 'brand.400')(props),
|
||||
bgActive: mode('brand.600', 'brand.300')(props),
|
||||
color: mode('white', 'brand.900')(props),
|
||||
boxColorFocus: mode('brand.100', 'brand.600')(props),
|
||||
}),
|
||||
'@secondary': (props) =>
|
||||
customVariant({
|
||||
theme: props.theme,
|
||||
bg: mode('brand.100', 'brand.900')(props),
|
||||
bgHover: mode('brand.200', 'brand.800')(props),
|
||||
bgActive: mode('brand.300', 'brand.700')(props),
|
||||
color: mode('brand.700', 'brand.50')(props),
|
||||
colorHover: mode('brand.800', 'brand.100')(props),
|
||||
boxColorFocus: mode('brand.900', 'brand.300')(props),
|
||||
}),
|
||||
'@danger': (props) =>
|
||||
customVariant({
|
||||
theme: props.theme,
|
||||
bg: mode('error.600', 'error.300')(props),
|
||||
bgHover: mode('error.700', 'error.400')(props),
|
||||
bgActive: mode('error.600', 'error.300')(props),
|
||||
color: mode('white', 'error.900')(props),
|
||||
boxColorFocus: mode('error.900', 'error.500')(props),
|
||||
}),
|
||||
|
||||
'@progress': (props) => ({
|
||||
...customVariant({
|
||||
theme: props.theme,
|
||||
bg: mode(`${props.colorScheme}.500`, `${props.colorScheme}.300`)(props),
|
||||
bgHover: mode(
|
||||
`${props.colorScheme}.600`,
|
||||
`${props.colorScheme}.400`
|
||||
)(props),
|
||||
bgActive: mode(
|
||||
`${props.colorScheme}.700`,
|
||||
`${props.colorScheme}.500`
|
||||
)(props),
|
||||
color: mode('white', `${props.colorScheme}.900`)(props),
|
||||
boxColorFocus: mode(
|
||||
`${props.colorScheme}.900`,
|
||||
`${props.colorScheme}.600`
|
||||
)(props),
|
||||
}),
|
||||
overflow: 'hidden',
|
||||
|
||||
_after: !props.isLoading
|
||||
? {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
transform: 'translateX(-100%)',
|
||||
bgGradient: `linear(90deg, ${transparentize(
|
||||
`${props.colorScheme}.100`,
|
||||
0
|
||||
)(props.theme)} 0, ${transparentize(
|
||||
`${props.colorScheme}.100`,
|
||||
0.2
|
||||
)(props.theme)} 20%, ${transparentize(
|
||||
`${props.colorScheme}.100`,
|
||||
0.5
|
||||
)(props.theme)} 60%, ${transparentize(
|
||||
`${props.colorScheme}.100`,
|
||||
0
|
||||
)(props.theme)})`,
|
||||
animation: `${shimmer} 3s infinite`,
|
||||
content: '""',
|
||||
}
|
||||
: undefined,
|
||||
}),
|
||||
'@menu': (props) => ({
|
||||
bg: mode('back.100', 'back.800')(props),
|
||||
color: mode('brand.900', 'brand.100')(props),
|
||||
borderRadius: 0,
|
||||
border: 0,
|
||||
_hover: { background: mode('back.300', 'back.600')(props) },
|
||||
_focus: { border: 'none' },
|
||||
fontSize: '20px',
|
||||
textTransform: 'uppercase',
|
||||
}),
|
||||
|
||||
// Default variants
|
||||
solid: (props) => ({
|
||||
bg:
|
||||
props.colorScheme === 'gray'
|
||||
? mode('gray.100', 'whiteAlpha.100')(props)
|
||||
: `${props.colorScheme}.600`,
|
||||
_hover: {
|
||||
bg:
|
||||
props.colorScheme === 'gray'
|
||||
? mode('gray.200', 'whiteAlpha.200')(props)
|
||||
: `${props.colorScheme}.700`,
|
||||
},
|
||||
_focus: {
|
||||
boxShadow: `outline-${props.colorScheme}`,
|
||||
},
|
||||
}),
|
||||
light: (props) => ({
|
||||
bg:
|
||||
props.colorScheme === 'gray'
|
||||
? mode('gray.100', 'whiteAlpha.100')(props)
|
||||
: `${props.colorScheme}.100`,
|
||||
color:
|
||||
props.colorScheme === 'gray'
|
||||
? mode('gray.700', 'whiteAlpha.700')(props)
|
||||
: `${props.colorScheme}.700`,
|
||||
_hover: {
|
||||
bg:
|
||||
props.colorScheme === 'gray'
|
||||
? mode('gray.200', 'whiteAlpha.200')(props)
|
||||
: `${props.colorScheme}.200`,
|
||||
color:
|
||||
props.colorScheme === 'gray'
|
||||
? mode('gray.800', 'whiteAlpha.800')(props)
|
||||
: `${props.colorScheme}.800`,
|
||||
},
|
||||
_focus: {
|
||||
boxShadow: `outline-${props.colorScheme}`,
|
||||
},
|
||||
}),
|
||||
ghost: (props) => ({
|
||||
bg: transparentize(`${props.colorScheme}.50`, 0.05)(props.theme),
|
||||
borderRadius: 'full',
|
||||
_hover: {
|
||||
bg: transparentize(`${props.colorScheme}.50`, 0.15)(props.theme),
|
||||
},
|
||||
_focus: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
5
front2/src/theme/components/checkbox.ts
Normal file
5
front2/src/theme/components/checkbox.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
defaultProps: {
|
||||
colorScheme: 'brand',
|
||||
},
|
||||
};
|
19
front2/src/theme/components/flex.ts
Normal file
19
front2/src/theme/components/flex.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { defineStyleConfig } from '@chakra-ui/react';
|
||||
import { mode } from '@chakra-ui/theme-tools';
|
||||
|
||||
|
||||
const flexTheme = defineStyleConfig({
|
||||
variants: {
|
||||
'@menu': (props) => ({
|
||||
bg: mode('back.100', 'back.800')(props),
|
||||
color: mode('brand.900', 'brand.100')(props),
|
||||
borderRadius: 0,
|
||||
border: 0,
|
||||
_hover: { background: mode('back.300', 'back.600')(props) },
|
||||
_focus: { border: 'none' },
|
||||
fontSize: '20px',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export default flexTheme;
|
12
front2/src/theme/components/index.ts
Normal file
12
front2/src/theme/components/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export { default as Badge } from './badge';
|
||||
export { default as Button } from './button';
|
||||
export { default as Checkbox } from './checkbox';
|
||||
export { default as Input } from './input';
|
||||
export { default as NumberInput } from './numberInput';
|
||||
export { default as Popover } from './popover';
|
||||
export { default as Radio } from './radio';
|
||||
export { default as Select } from './select';
|
||||
export { default as Switch } from './switch';
|
||||
export { default as Textarea } from './textarea';
|
||||
export { default as Modal } from './modal';
|
||||
export { default as Flex } from './flex';
|
25
front2/src/theme/components/input.ts
Normal file
25
front2/src/theme/components/input.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { getColor, mode } from '@chakra-ui/theme-tools';
|
||||
|
||||
export default {
|
||||
variants: {
|
||||
outline: (props) => {
|
||||
const focusBorderColor = getColor(
|
||||
props.theme,
|
||||
props.focusBorderColor
|
||||
? props.focusBorderColor
|
||||
: mode('brand.500', 'brand.300')(props)
|
||||
);
|
||||
return {
|
||||
field: {
|
||||
bg: mode('gray.50', 'whiteAlpha.50')(props),
|
||||
borderColor: mode('gray.200', 'whiteAlpha.100')(props),
|
||||
color: mode('gray.800', 'gray.50')(props),
|
||||
_focus: {
|
||||
borderColor: focusBorderColor,
|
||||
boxShadow: `0 0 0 1px ${focusBorderColor}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
17
front2/src/theme/components/modal.ts
Normal file
17
front2/src/theme/components/modal.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { modalAnatomy as parts } from '@chakra-ui/anatomy';
|
||||
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
|
||||
|
||||
const { definePartsStyle, defineMultiStyleConfig } =
|
||||
createMultiStyleConfigHelpers(parts.keys);
|
||||
|
||||
const baseStyle = definePartsStyle({
|
||||
header: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
const modalTheme = defineMultiStyleConfig({
|
||||
baseStyle,
|
||||
});
|
||||
|
||||
export default modalTheme;
|
33
front2/src/theme/components/numberInput.ts
Normal file
33
front2/src/theme/components/numberInput.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { numberInputAnatomy } from '@chakra-ui/anatomy';
|
||||
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
|
||||
import { getColor, mode } from '@chakra-ui/theme-tools';
|
||||
|
||||
const { definePartsStyle, defineMultiStyleConfig } =
|
||||
createMultiStyleConfigHelpers(numberInputAnatomy.keys);
|
||||
|
||||
const baseStyle = definePartsStyle((props) => {
|
||||
const focusBorderColor = getColor(
|
||||
props.theme,
|
||||
props.focusBorderColor
|
||||
? props.focusBorderColor
|
||||
: mode('brand.500', 'brand.300')(props)
|
||||
);
|
||||
|
||||
return {
|
||||
field: {
|
||||
border: 0,
|
||||
_focusVisible: {
|
||||
borderColor: focusBorderColor,
|
||||
boxShadow: `0 0 0 1px ${focusBorderColor}`,
|
||||
ring: '1px',
|
||||
ringColor: focusBorderColor,
|
||||
ringOffset: '1px',
|
||||
ringOffsetColor: focusBorderColor,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default defineMultiStyleConfig({
|
||||
baseStyle,
|
||||
});
|
77
front2/src/theme/components/popover.ts
Normal file
77
front2/src/theme/components/popover.ts
Normal file
@ -0,0 +1,77 @@
|
||||
export default {
|
||||
sizes: {
|
||||
'3xs': {
|
||||
content: {
|
||||
width: '3xs',
|
||||
},
|
||||
},
|
||||
'2xs': {
|
||||
content: {
|
||||
width: '2xs',
|
||||
},
|
||||
},
|
||||
xs: {
|
||||
content: {
|
||||
width: 'xs',
|
||||
},
|
||||
},
|
||||
sm: {
|
||||
content: {
|
||||
width: 'sm',
|
||||
},
|
||||
},
|
||||
md: {
|
||||
content: {
|
||||
width: 'md',
|
||||
},
|
||||
},
|
||||
lg: {
|
||||
content: {
|
||||
width: 'lg',
|
||||
},
|
||||
},
|
||||
xl: {
|
||||
content: {
|
||||
width: 'xl',
|
||||
},
|
||||
},
|
||||
'2xl': {
|
||||
content: {
|
||||
width: '2xl',
|
||||
},
|
||||
},
|
||||
'3xl': {
|
||||
content: {
|
||||
width: '3xl',
|
||||
},
|
||||
},
|
||||
'4xl': {
|
||||
content: {
|
||||
width: '4xl',
|
||||
},
|
||||
},
|
||||
'5xl': {
|
||||
content: {
|
||||
width: '5xl',
|
||||
},
|
||||
},
|
||||
'6xl': {
|
||||
content: {
|
||||
width: '6xl',
|
||||
},
|
||||
},
|
||||
'7xl': {
|
||||
content: {
|
||||
width: '7xl',
|
||||
},
|
||||
},
|
||||
'8xl': {
|
||||
content: {
|
||||
width: '8xl',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultProps: {
|
||||
size: 'xs',
|
||||
},
|
||||
};
|
5
front2/src/theme/components/radio.ts
Normal file
5
front2/src/theme/components/radio.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
defaultProps: {
|
||||
colorScheme: 'brand',
|
||||
},
|
||||
};
|
24
front2/src/theme/components/select.ts
Normal file
24
front2/src/theme/components/select.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { getColor, mode } from '@chakra-ui/theme-tools';
|
||||
|
||||
export default {
|
||||
variants: {
|
||||
outline: (props) => {
|
||||
const focusBorderColor = getColor(
|
||||
props.theme,
|
||||
props.focusBorderColor
|
||||
? props.focusBorderColor
|
||||
: mode('brand.500', 'brand.300')(props)
|
||||
);
|
||||
return {
|
||||
field: {
|
||||
bg: mode('gray.50', 'whiteAlpha.50')(props),
|
||||
borderColor: mode('blackAlpha.100', 'whiteAlpha.100')(props),
|
||||
_focus: {
|
||||
borderColor: focusBorderColor,
|
||||
boxShadow: `0 0 0 1px ${focusBorderColor}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
5
front2/src/theme/components/switch.ts
Normal file
5
front2/src/theme/components/switch.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
defaultProps: {
|
||||
colorScheme: 'brand',
|
||||
},
|
||||
};
|
22
front2/src/theme/components/textarea.ts
Normal file
22
front2/src/theme/components/textarea.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { getColor, mode } from '@chakra-ui/theme-tools';
|
||||
|
||||
export default {
|
||||
variants: {
|
||||
outline: (props) => {
|
||||
const focusBorderColor = getColor(
|
||||
props.theme,
|
||||
props.focusBorderColor
|
||||
? props.focusBorderColor
|
||||
: mode('brand.500', 'brand.300')(props)
|
||||
);
|
||||
return {
|
||||
bg: mode('gray.50', 'whiteAlpha.50')(props),
|
||||
borderColor: mode('blackAlpha.100', 'whiteAlpha.100')(props),
|
||||
_focus: {
|
||||
borderColor: focusBorderColor,
|
||||
boxShadow: `0 0 0 1px ${focusBorderColor}`,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
127
front2/src/theme/foundations/colors.ts
Normal file
127
front2/src/theme/foundations/colors.ts
Normal file
@ -0,0 +1,127 @@
|
||||
type ThemeModel = {
|
||||
50: string;
|
||||
100: string;
|
||||
200: string;
|
||||
300: string;
|
||||
400: string;
|
||||
500: string;
|
||||
600: string;
|
||||
700: string;
|
||||
800: string;
|
||||
900: string;
|
||||
};
|
||||
const isLightMode = false;
|
||||
const reverseColor = (data: ThemeModel) => {
|
||||
return {
|
||||
50: data[900],
|
||||
100: data[800],
|
||||
200: data[700],
|
||||
300: data[600],
|
||||
400: data[500],
|
||||
500: data[400],
|
||||
600: data[300],
|
||||
700: data[200],
|
||||
800: data[100],
|
||||
900: data[50],
|
||||
};
|
||||
};
|
||||
|
||||
const back = {
|
||||
50: '#ebf4fa',
|
||||
100: '#d1dbe0',
|
||||
200: '#b6c2c9',
|
||||
300: '#99aab4',
|
||||
400: '#7c939e',
|
||||
500: '#637985',
|
||||
600: '#4d5e67',
|
||||
700: '#37444a',
|
||||
800: '#1f292e',
|
||||
900: '#020f12',
|
||||
};
|
||||
|
||||
const brand = {
|
||||
50: '#e3edff',
|
||||
100: '#b6c9fd',
|
||||
200: '#88a5f7',
|
||||
300: '#5a81f2',
|
||||
400: '#2c5ded',
|
||||
500: '#1543d4',
|
||||
600: '#0d34a5',
|
||||
700: '#062577',
|
||||
800: '#02164a',
|
||||
900: '#00071e',
|
||||
};
|
||||
const normalText = {
|
||||
50: '#f2f2f2',
|
||||
100: '#d9d9d9',
|
||||
200: '#bfbfbf',
|
||||
300: '#a6a6a6',
|
||||
400: '#8c8c8c',
|
||||
500: '#737373',
|
||||
600: '#595959',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#0d0d0d',
|
||||
};
|
||||
|
||||
const green = {
|
||||
50: '#f0fdf4',
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
300: '#86efac',
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
800: '#166534',
|
||||
900: '#14532d',
|
||||
};
|
||||
const blue = {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
};
|
||||
|
||||
const orange = {
|
||||
50: '#fff7ed',
|
||||
100: '#ffedd5',
|
||||
200: '#fed7aa',
|
||||
300: '#fdba74',
|
||||
400: '#fb923c',
|
||||
500: '#f97316',
|
||||
600: '#ea580c',
|
||||
700: '#c2410c',
|
||||
800: '#9a3412',
|
||||
900: '#7c2d12',
|
||||
};
|
||||
const red = {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
200: '#fecaca',
|
||||
300: '#fca5a5',
|
||||
400: '#f87171',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
800: '#991b1b',
|
||||
900: '#7f1d1d',
|
||||
};
|
||||
|
||||
export const colors = {
|
||||
// Update me with other Tailwind colors or with https://smart-swatch.netlify.app/
|
||||
|
||||
brand: brand,
|
||||
back: back,
|
||||
text: normalText,
|
||||
/// ????
|
||||
success: green,
|
||||
error: red,
|
||||
warning: orange,
|
||||
} as const;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user