diff --git a/.github/workflows/CD-Pipeline.yml b/.github/workflows/CD-Pipeline.yml
new file mode 100644
index 0000000..95a55f5
--- /dev/null
+++ b/.github/workflows/CD-Pipeline.yml
@@ -0,0 +1,13 @@
+name: Continuous Deployment Pipeline
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ Web_Deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Trigger Deployment
+ run: curl https://api.render.com/deploy/srv-${{ secrets.RENDER_SERVICE_ID }}?key=${{ secrets.RENDER_API_KEY }}
\ No newline at end of file
diff --git a/.github/workflows/Deployment-Pipeline.yml b/.github/workflows/CI-Pipeline.yml
similarity index 83%
rename from .github/workflows/Deployment-Pipeline.yml
rename to .github/workflows/CI-Pipeline.yml
index 44bb76b..8d022a8 100644
--- a/.github/workflows/Deployment-Pipeline.yml
+++ b/.github/workflows/CI-Pipeline.yml
@@ -1,10 +1,6 @@
-name: FullStack Deployment Pipeline
+name: Continuous Integration Pipeline
-on:
- push:
- branches:
- - main
-
+on:
pull_request:
branches: [main]
types: [opened, synchronize]
@@ -28,6 +24,9 @@ jobs:
- name: Check style
run: npm run lint
+ - name: Check Security
+ run: npx eslint .
+
Backend-Pipeline:
runs-on: ubuntu-latest
defaults:
@@ -46,6 +45,9 @@ jobs:
- name: Check style
run: npm run lint
+ - name: Check security
+ run: npx eslint .
+
- name: Run tests
run: npm run test
@@ -100,12 +102,4 @@ jobs:
with:
name: playwright-report
path: playwright-report/
- retention-days: 30
-
- Web_Deploy:
- runs-on: ubuntu-latest
- needs: [Frontend-Pipeline,Backend-Pipeline,Playwright-E2E-Tests]
- steps:
- - uses: actions/checkout@v4
- - name: Trigger Deployment
- run: curl https://api.render.com/deploy/srv-${{ secrets.RENDER_SERVICE_ID }}?key=${{ secrets.RENDER_API_KEY }}
+ retention-days: 30
\ No newline at end of file
diff --git a/.github/workflows/hello.yml b/.github/workflows/hello.yml
deleted file mode 100644
index 02f96d2..0000000
--- a/.github/workflows/hello.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Hello World!
-
-on:
- push:
- branches:
- - main
-
- pull_request:
- branches: [main]
- types: [opened, synchronize]
-
-jobs:
- hello_world_job:
- runs-on: ubuntu-latest
- steps:
- - name: Say Hello
- run: |
- echo "Hello World!"
- echo "Current date and time:"
- date
diff --git a/back/eslint.config.mjs b/back/eslint.config.mjs
index a2838ab..30ad90e 100644
--- a/back/eslint.config.mjs
+++ b/back/eslint.config.mjs
@@ -1,16 +1,12 @@
// @ts-check
import eslint from '@eslint/js';
-import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import globals from 'globals';
import tseslint from 'typescript-eslint';
+import pluginSecurity from 'eslint-plugin-security';
export default tseslint.config(
- {
- ignores: ['eslint.config.mjs'],
- },
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
- eslintPluginPrettierRecommended,
{
languageOptions: {
globals: {
@@ -23,12 +19,17 @@ export default tseslint.config(
tsconfigRootDir: import.meta.dirname,
},
},
+ plugins: {
+ security: pluginSecurity,
+ }
},
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-floating-promises': 'warn',
- '@typescript-eslint/no-unsafe-argument': 'warn'
+ '@typescript-eslint/no-unsafe-argument': 'warn',
+ ...pluginSecurity.configs.recommended.rules,
},
},
+ { ignores: ['eslint.config.mjs', "node_modules", "dist", "bonsai", "coverage", "public"], },
);
\ No newline at end of file
diff --git a/back/package-lock.json b/back/package-lock.json
index 06de57e..71c211b 100644
--- a/back/package-lock.json
+++ b/back/package-lock.json
@@ -51,6 +51,7 @@
"eslint": "^9.39.3",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
+ "eslint-plugin-security": "^4.0.0",
"globals": "^16.0.0",
"jest": "^29.7.0",
"prettier": "^3.4.2",
@@ -3042,12 +3043,12 @@
}
},
"node_modules/@nestjs/common": {
- "version": "11.1.14",
- "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.14.tgz",
- "integrity": "sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==",
+ "version": "11.1.17",
+ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.17.tgz",
+ "integrity": "sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==",
"license": "MIT",
"dependencies": {
- "file-type": "21.3.0",
+ "file-type": "21.3.2",
"iterare": "1.2.1",
"load-esm": "1.0.3",
"tslib": "2.8.1",
@@ -3217,14 +3218,14 @@
}
},
"node_modules/@nestjs/platform-express": {
- "version": "11.1.15",
- "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.15.tgz",
- "integrity": "sha512-sR42dQ5fPy4NGbnhT4mRIJLL0h2eNY4KpLkcfd27vg5XdTjQQ07wkQORrw6rAkWkBvMRr0dmhAcUBnXpe7ro5Q==",
+ "version": "11.1.17",
+ "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.17.tgz",
+ "integrity": "sha512-mAf4eOsSBsTOn/VbrUO1gsjW6dVh91qqXPMXun4dN8SnNjf7PTQagM9o8d6ab8ZBpNe6UdZftdrZoDetU+n4Qg==",
"license": "MIT",
"dependencies": {
"cors": "2.8.6",
"express": "5.2.1",
- "multer": "2.1.0",
+ "multer": "2.1.1",
"path-to-regexp": "8.3.0",
"tslib": "2.8.1"
},
@@ -7234,6 +7235,22 @@
}
}
},
+ "node_modules/eslint-plugin-security": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-4.0.0.tgz",
+ "integrity": "sha512-tfuQT8K/Li1ZxhFzyD8wPIKtlzZxqBcPr9q0jFMQ77wWAbKBVEhaMPVQRTMTvCMUDhwBe5vPVqQPwAGk/ASfxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-regex": "^2.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/eslint-scope": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
@@ -7718,9 +7735,9 @@
}
},
"node_modules/file-type": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz",
- "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==",
+ "version": "21.3.2",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.2.tgz",
+ "integrity": "sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==",
"license": "MIT",
"dependencies": {
"@tokenizer/inflate": "^0.4.1",
@@ -7845,9 +7862,9 @@
}
},
"node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"dev": true,
"license": "ISC"
},
@@ -10848,9 +10865,9 @@
"license": "MIT"
},
"node_modules/multer": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.0.tgz",
- "integrity": "sha512-TBm6j41rxNohqawsxlsWsNNh/VdV4QFXcBvRcPhXaA05EZ79z0qJ2bQFpync6JBoHTeNY5Q1JpG7AlTjdlfAEA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
+ "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
"license": "MIT",
"dependencies": {
"append-field": "^1.0.0",
@@ -11858,6 +11875,16 @@
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
"license": "Apache-2.0"
},
+ "node_modules/regexp-tree": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
+ "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "regexp-tree": "bin/regexp-tree"
+ }
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -12077,6 +12104,16 @@
],
"license": "MIT"
},
+ "node_modules/safe-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
+ "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regexp-tree": "~0.1.1"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -12200,9 +12237,9 @@
}
},
"node_modules/sequelize": {
- "version": "6.37.7",
- "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz",
- "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==",
+ "version": "6.37.8",
+ "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.8.tgz",
+ "integrity": "sha512-HJ0IQFqcTsTiqbEgiuioYFMSD00TP6Cz7zoTti+zVVBwVe9fEhev9cH6WnM3XU31+ABS356durAb99ZuOthnKw==",
"funding": [
{
"type": "opencollective",
@@ -14363,9 +14400,9 @@
}
},
"node_modules/yauzl": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz",
- "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.1.tgz",
+ "integrity": "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/back/package.json b/back/package.json
index b3eb14d..69a9912 100644
--- a/back/package.json
+++ b/back/package.json
@@ -62,6 +62,7 @@
"eslint": "^9.39.3",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
+ "eslint-plugin-security": "^4.0.0",
"globals": "^16.0.0",
"jest": "^29.7.0",
"prettier": "^3.4.2",
diff --git a/back/src/types/verify.ts b/back/src/types/verify.ts
index b679407..6ee50c4 100644
--- a/back/src/types/verify.ts
+++ b/back/src/types/verify.ts
@@ -18,8 +18,8 @@ export const isString = (text: unknown): text is string => {
};
export const isNumber = (value: unknown): value is number => {
- if (typeof value === 'string')
- return value.trim() !== '' && /^-?\d+(\.\d+)?$/.test(value.trim());
+ if (typeof value === 'string')
+ return value.trim() !== '' && !isNaN(Number(value.trim()));
return (
value !== null &&
value !== undefined &&
diff --git a/front/eslint.config.js b/front/eslint.config.js
index 092408a..86a20cb 100644
--- a/front/eslint.config.js
+++ b/front/eslint.config.js
@@ -3,11 +3,12 @@ import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
+import pluginSecurity from 'eslint-plugin-security';
export default tseslint.config(
- { ignores: ['dist'] },
+ js.configs.recommended,
+ ...tseslint.configs.recommended,
{
- extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
@@ -16,13 +17,16 @@ export default tseslint.config(
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
+ 'security': pluginSecurity
},
rules: {
...reactHooks.configs.recommended.rules,
+ ...pluginSecurity.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
+ { ignores: ["node_modules", "build", 'dist'] },
)
diff --git a/front/package-lock.json b/front/package-lock.json
index 96043c0..6b0f7dd 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -45,6 +45,7 @@
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
+ "eslint-plugin-security": "^4.0.0",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
@@ -4259,6 +4260,22 @@
"eslint": ">=8.40"
}
},
+ "node_modules/eslint-plugin-security": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-4.0.0.tgz",
+ "integrity": "sha512-tfuQT8K/Li1ZxhFzyD8wPIKtlzZxqBcPr9q0jFMQ77wWAbKBVEhaMPVQRTMTvCMUDhwBe5vPVqQPwAGk/ASfxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-regex": "^2.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/eslint-scope": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
@@ -4449,9 +4466,9 @@
}
},
"node_modules/flatted": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
- "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"dev": true,
"license": "ISC"
},
@@ -5742,6 +5759,16 @@
"redux": "^5.0.0"
}
},
+ "node_modules/regexp-tree": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
+ "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "regexp-tree": "bin/regexp-tree"
+ }
+ },
"node_modules/rehackt": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
@@ -5860,6 +5887,16 @@
],
"license": "MIT"
},
+ "node_modules/safe-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
+ "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regexp-tree": "~0.1.1"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -6291,9 +6328,9 @@
}
},
"node_modules/undici": {
- "version": "7.22.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
- "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==",
+ "version": "7.24.4",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz",
+ "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
diff --git a/front/package.json b/front/package.json
index 816e845..e43d70b 100644
--- a/front/package.json
+++ b/front/package.json
@@ -47,6 +47,7 @@
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
+ "eslint-plugin-security": "^4.0.0",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
diff --git a/front/src/apolloClient.ts b/front/src/apolloClient.ts
index 2c5d49a..d064a0c 100644
--- a/front/src/apolloClient.ts
+++ b/front/src/apolloClient.ts
@@ -1,4 +1,7 @@
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
+import { SongResponse } from "./types/songTypes";
+import { ArtistResponse } from "./types/artistTypes";
+import { AlbumResponse } from "./types/albumTypes";
export const client = new ApolloClient({
cache: new InMemoryCache({
@@ -8,40 +11,33 @@ export const client = new ApolloClient({
getAllGenreSongs: {
keyArgs: ["genre"],
merge(existing, incoming, { args }) {
- const page = args?.page ?? 1;
- const limit = args?.limit ?? 2;
+ const page: number = args?.page ?? 1;
+ const limit: number = args?.limit ?? 2;
const offset = (page - 1) * limit;
- const merged = existing ? existing.slice(0) : [];
-
- for (let i = 0; i < incoming.length; i++) {
- merged[offset + i] = incoming[i];
- };
+ const merged: SongResponse[] = existing ? existing.slice(0) : [];
+ merged.splice(offset, incoming.length, ...incoming);
return merged;
}
},
getAllArtists: {
keyArgs: false,
merge(existing, incoming, { args }) {
- const page = args?.page ?? 1;
- const limit = args?.limit ?? 20;
+ const page: number = args?.page ?? 1;
+ const limit: number = args?.limit ?? 20;
const offset = (page - 1) * limit;
- const merged = existing ? existing.slice(0) : [];
- for (let i = 0; i < incoming.length; i++) {
- merged[offset + i] = incoming[i]
- }
+ const merged: ArtistResponse[] = existing ? existing.slice(0) : [];
+ merged.splice(offset, incoming.length, ...incoming);
return merged;
}
},
getAlbums: {
keyArgs: false,
merge(existing, incoming, {args}) {
- const page = args?.page ?? 1;
- const limit = args?.limit ?? 20;
+ const page: number = args?.page ?? 1;
+ const limit: number = args?.limit ?? 20;
const offset = (page - 1) * limit;
- const merged = existing ? existing.slice(0) : [];
- for (let i=0; i < incoming.length; i++) {
- merged[offset + i] = incoming[i]
- }
+ const merged: AlbumResponse[] = existing ? existing.slice(0) : [];
+ merged.splice(offset, incoming.length, ...incoming);
return merged;
}
}
diff --git a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Sliders/SliderTags.tsx b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Sliders/SliderTags.tsx
index fd232a2..56ba80c 100644
--- a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Sliders/SliderTags.tsx
+++ b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Sliders/SliderTags.tsx
@@ -1,5 +1,5 @@
import { SLIDER_TAGS_GAP_SIZES, SLIDER_TAGS_PX_SIZES } from "@/components/constants/TopBarC";
-import { TagSliderStyles } from "@/dynamicCSS/TagsSliderStyles";
+import { MapSliderTagStyles } from "@/dynamicCSS/TagsSliderStyles";
import { SliderLabels } from "@/types/RecDataTypes";
import { Flex, For, Tag } from "@chakra-ui/react";
@@ -7,13 +7,23 @@ const SliderTags = ({ labels } : { labels: SliderLabels[] }) => {
return(
- {(label) => (
+ {(label) => {
+ let labelStyle = MapSliderTagStyles.get(label);
+ if (labelStyle === undefined) {
+ labelStyle = {
+ bg: "gray.500",
+ border: "0 0 0 2px var(--chakra-colors-gray-500), 0 0 8px var(--chakra-colors-gray-500)"
+ }
+ };
+ return (
+ bg={labelStyle.bg} py={1} fontWeight="800"
+ boxShadow={labelStyle.border} letterSpacing="wide"
+ px={SLIDER_TAGS_PX_SIZES} fontSize="'Barlow', sans-serif">
{label}
- )}
+ )
+ }}
);
diff --git a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/LeftSwitchTag.tsx b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/LeftSwitchTag.tsx
index 69cc61e..3ee2a5f 100644
--- a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/LeftSwitchTag.tsx
+++ b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/LeftSwitchTag.tsx
@@ -1,14 +1,15 @@
import { Tag } from "@chakra-ui/react";
import { Tooltip } from "@/components/ui/tooltip";
import { SwitchLabelType } from "@/types/RecDataTypes";
-import { SwitchStyles } from "@/dynamicCSS/SwitchStyles";
+import { defaultSwitchStyle, MapSwitchStyles } from "@/dynamicCSS/SwitchStyles";
import { LEFT_SWITCH_PX, tagLBG } from "@/components/constants/TopBarC";
const LeftSwitchTag = ({ label, description } : { label: SwitchLabelType, description: string }) => {
+ const leftSwitchStyle = MapSwitchStyles.get(label) || defaultSwitchStyle;
return(
-
{label}
diff --git a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/RightSwitchTag.tsx b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/RightSwitchTag.tsx
index 875573d..d6fa321 100644
--- a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/RightSwitchTag.tsx
+++ b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/RightSwitchTag.tsx
@@ -1,16 +1,17 @@
import { Tag } from "@chakra-ui/react";
import { Tooltip } from "@/components/ui/tooltip";
import { SwitchLabelType } from "@/types/RecDataTypes";
-import { SwitchStyles } from "@/dynamicCSS/SwitchStyles";
+import { defaultSwitchStyle, MapSwitchStyles } from "@/dynamicCSS/SwitchStyles";
import { RIGHT_SWITCH_PX, tagRBG } from "@/components/constants/TopBarC";
const RightSwitchTag = ({ label, description } : { label: SwitchLabelType, description: string }) => {
+ const switchStyle = MapSwitchStyles.get(label) || defaultSwitchStyle;
return(
+ fontWeight="600" px={RIGHT_SWITCH_PX} py={2} color="white" bg={switchStyle.tagBg}
+ borderRadius="full" boxShadow={switchStyle.border}>
{label}
diff --git a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/SwitchButton.tsx b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/SwitchButton.tsx
index 95bcb86..b59dfdd 100644
--- a/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/SwitchButton.tsx
+++ b/front/src/components/MainLayout/TopBar/LaraRecommendModal/ModalComponents/Switch/SwitchButton.tsx
@@ -1,17 +1,21 @@
import { Box, Heading, Flex, Switch } from "@chakra-ui/react";
import { useAppDispatch } from "@/components/Utils/redux-hooks";
-import { SwitchStyles } from "@/dynamicCSS/SwitchStyles";
import { SwitchOptionsType, SwitchLabelType } from "@/types/RecDataTypes";
import LeftSwitchTag from "./LeftSwitchTag";
import RightSwitchTag from "./RightSwitchTag";
import ControlSwitchStyle from "./ControlSwitchStyle";
import useSwitchValue from "@/components/Utils/hooks/useSwitchValue";
import { BLUE_SHADOW_MODAL, LARA_OPT_SIZES, SWITCH_BTN_TRANSITION } from "@/components/constants/TopBarC";
+import { defaultSwitchStyle, MapSwitchStyles } from "@/dynamicCSS/SwitchStyles";
const SwitchButton = ({ params }:{ params: SwitchOptionsType }) => {
const { switchTagValue, toggleSwitchValue } = useSwitchValue(params.title, params.labels[0].label,
params.labels[1].label, params.setValue);
+
+ const leftSwitchStyle = MapSwitchStyles.get(params.labels[0].label) || defaultSwitchStyle;
+ const rightSwitchStyle = MapSwitchStyles.get(params.labels[1].label) || defaultSwitchStyle;
const dispatch = useAppDispatch();
+
return(
({ params }:{ params:
dispatch(toggleSwitchValue())}>
-
+
diff --git a/front/src/dynamicCSS/SliderStyles.ts b/front/src/dynamicCSS/SliderStyles.ts
index b6857ab..50b601d 100644
--- a/front/src/dynamicCSS/SliderStyles.ts
+++ b/front/src/dynamicCSS/SliderStyles.ts
@@ -5,19 +5,19 @@ import { FaWind, FaLeaf } from "react-icons/fa";
import { FaHeartCircleBolt, FaCloud } from "react-icons/fa6";
import { AiFillThunderbolt } from "react-icons/ai";
import { GiGuitarHead, GiHeartBeats, GiLotus } from "react-icons/gi";
-import { SliderStylesOptions } from "@/types/modalCssTypes";
+import { SliderStylesProps, SliderType } from "@/types/modalCssTypes";
-export const SliderStyles: SliderStylesOptions = {
- Energy: {
+export const MapSliderStyles = new Map ([
+ ["Energy", {
threshold: [0.30, 0.70],
SliderBg: ["green.500", "yellow.600", "red.600"],
Icon: [FaCloud, BsFire, AiFillThunderbolt],
IconColor: ["green.400", "red.500", "yellow.500"],
IconBorder: ["cyan.600", "orange.600", "yellow.600"],
shadow: ["0 0 8px var(--chakra-colors-green-500)", "0 0 8px var(--chakra-colors-yellow-600)",
- "0 0 8px var(--chakra-colors-red-600)" ]
- },
- Danceability: {
+ "0 0 8px var(--chakra-colors-red-600)" ],
+ }],
+ ["Danceability", {
threshold: [0.30, 0.70],
SliderBg: ["blue.500", "teal.500", "red.600"],
Icon: [GiLotus, GiHeartBeats, RiFlashlightFill],
@@ -25,8 +25,8 @@ export const SliderStyles: SliderStylesOptions = {
IconBorder: ["cyan.600", "cyan.500", "yellow.600"],
shadow: ["0 0 8px var(--chakra-colors-blue-500)", "0 0 8px var(--chakra-colors-teal-500)",
"0 0 8px var(--chakra-colors-red-600)" ]
- },
- Tempo: {
+ }],
+ ["Tempo", {
threshold: [75, 160],
SliderBg: ["yellow.600", "green.500", "purple.500"],
Icon: [FaWind, GiGuitarHead, FaHeartCircleBolt],
@@ -34,8 +34,8 @@ export const SliderStyles: SliderStylesOptions = {
IconBorder: ["orange.500", "teal.500", "purple.600"],
shadow: ["0 0 8px var(--chakra-colors-yellow-600)", "0 0 8px var(--chakra-colors-green-500)",
"0 0 8px var(--chakra-colors-purple-500)"]
- },
- Sentiment: {
+ }],
+ ["Sentiment", {
threshold: [0.30, 0.70],
SliderBg: ["cyan.600", "orange.600", "pink.600"],
Icon: [FaLeaf, RiEmotionHappyFill, BiSolidHappyBeaming],
@@ -43,5 +43,15 @@ export const SliderStyles: SliderStylesOptions = {
IconBorder: ["cyan.500", "orange.600", "red.500"],
shadow: ["0 0 8px var(--chakra-colors-cyan-600)", "0 0 8px var(--chakra-colors-orange-600)",
"0 0 8px var(--chakra-colors-pink-600)" ]
- }
-};
\ No newline at end of file
+ }]
+]);
+
+export const defaultSliderStyle = {
+ threshold: [0.30, 0.70],
+ SliderBg: ["green.500", "yellow.600", "red.600"],
+ Icon: [FaCloud, BsFire, AiFillThunderbolt],
+ IconColor: ["green.400", "red.500", "yellow.500"],
+ IconBorder: ["cyan.600", "orange.600", "yellow.600"],
+ shadow: ["0 0 8px var(--chakra-colors-green-500)", "0 0 8px var(--chakra-colors-yellow-600)",
+ "0 0 8px var(--chakra-colors-red-600)" ]
+}
\ No newline at end of file
diff --git a/front/src/dynamicCSS/SwitchStyles.ts b/front/src/dynamicCSS/SwitchStyles.ts
index 551ddd5..d8b6b48 100644
--- a/front/src/dynamicCSS/SwitchStyles.ts
+++ b/front/src/dynamicCSS/SwitchStyles.ts
@@ -1,8 +1,8 @@
-import { SwitchTagsStyles } from "@/types/modalCssTypes";
+import { SwitchStyle, SwitchType } from "@/types/modalCssTypes";
import { FaMicrophoneAlt, FaGuitar, FaSmile, FaSadTear, FaMicrophone, FaHeadphones } from "react-icons/fa";
-export const SwitchStyles: SwitchTagsStyles = {
- Vocals: {
+export const MapSwitchStyles = new Map([
+ ["Vocals", {
tagBg: "pink.400",
ThumbBg: "pink.200",
ThumbBackgroundBg: "pink.400",
@@ -11,8 +11,8 @@ export const SwitchStyles: SwitchTagsStyles = {
ThumbBackgroundIcon: FaGuitar,
BackgroundIconColor: "cyan.500",
border: "0 0 0 2px var(--chakra-colors-pink-500), 0 0 8px var(--chakra-colors-pink-500)",
- },
- Instrumental: {
+ }],
+ ["Instrumental", {
tagBg: "cyan.500",
ThumbBg: "cyan.200",
ThumbBackgroundBg: "cyan.400",
@@ -21,8 +21,8 @@ export const SwitchStyles: SwitchTagsStyles = {
ThumbBackgroundIcon: FaMicrophone,
BackgroundIconColor: "pink.400",
border: "0 0 0 2px var(--chakra-colors-cyan-600), 0 0 8px var(--chakra-colors-cyan-600)"
- },
- Happy: {
+ }],
+ ["Happy", {
tagBg: "purple.400",
ThumbBg: "purple.200",
ThumbBackgroundBg: "purple.400",
@@ -31,8 +31,8 @@ export const SwitchStyles: SwitchTagsStyles = {
ThumbBackgroundIcon: FaSadTear,
BackgroundIconColor: "blue.500",
border: "0 0 0 2px var(--chakra-colors-purple-500), 0 0 8px var(--chakra-colors-purple-500)"
- },
- Sad: {
+ }],
+ ["Sad", {
tagBg: "blue.500",
ThumbBg: "blue.500",
ThumbBackgroundBg: "blue.400",
@@ -41,8 +41,8 @@ export const SwitchStyles: SwitchTagsStyles = {
ThumbBackgroundIcon: FaSmile,
BackgroundIconColor: "yellow.500",
border: "0 0 0 2px var(--chakra-colors-blue-600), 0 0 8px var(--chakra-colors-blue-600)"
- },
- Acoustic: {
+ }],
+ ["Acoustic", {
tagBg: "green.500",
ThumbBg: "green.200",
ThumbBackgroundBg: "green.500",
@@ -51,8 +51,8 @@ export const SwitchStyles: SwitchTagsStyles = {
ThumbBackgroundIcon: FaHeadphones,
BackgroundIconColor: "orange.300",
border: "0 0 0 2px var(--chakra-colors-green-600), 0 0 8px var(--chakra-colors-green-600)"
- },
- Electronic: {
+ }],
+ ["Electronic", {
tagBg: "orange.400",
ThumbBg: "orange.200",
ThumbBackgroundBg: "orange.400",
@@ -61,5 +61,16 @@ export const SwitchStyles: SwitchTagsStyles = {
ThumbBackgroundIcon: FaGuitar,
BackgroundIconColor: "green.600",
border: "0 0 0 2px var(--chakra-colors-orange-500), 0 0 8px var(--chakra-colors-orange-500)"
- },
-};
\ No newline at end of file
+ }]
+]);
+
+export const defaultSwitchStyle = {
+ tagBg: "gray.500",
+ ThumbBg: "gray.200",
+ ThumbBackgroundBg: "gray.400",
+ ThumbIcon: FaMicrophoneAlt,
+ IconColor: "gray.500",
+ ThumbBackgroundIcon: FaGuitar,
+ BackgroundIconColor: "cyan.500",
+ border: "0 0 0 2px var(--chakra-colors-gray-500), 0 0 8px var(--chakra-colors-gray-500)"
+}
\ No newline at end of file
diff --git a/front/src/dynamicCSS/TagsSliderStyles.ts b/front/src/dynamicCSS/TagsSliderStyles.ts
index df8aef8..49afdc8 100644
--- a/front/src/dynamicCSS/TagsSliderStyles.ts
+++ b/front/src/dynamicCSS/TagsSliderStyles.ts
@@ -1,52 +1,49 @@
-import { SliderTagsOptions } from "@/types/modalCssTypes";
+import { SliderTagsProps } from "@/types/modalCssTypes";
+import { SliderLabels } from "@/types/RecDataTypes";
-export const TagSliderStyles: SliderTagsOptions = {
- Relaxed: {
+export const MapSliderTagStyles = new Map ([
+ ["Relaxed", {
bg: "green.600",
border: "0 0 0 2px var(--chakra-colors-cyan-600), 0 0 8px var(--chakra-colors-cyan-600)",
- },
- Active: {
+ }],
+ ["Active", {
bg: "yellow.600",
border: "0 0 0 2px var(--chakra-colors-orange-500), 0 0 8px var(--chakra-colors-orange-500)"
- },
- Intense: {
+ }],
+ ["Intense", {
bg: "red.600",
- border: "0 0 0 2px var(--chakra-colors-orange-600), 0 0 8px var(--chakra-colors-orange-600)"
- },
- Calm: {
+ border: "0 0 0 2px var(--chakra-colors-orange-600), 0 0 8px var(--chakra-colors-orange-600)",
+ }],
+ ["Calm", {
bg: "blue.500",
border: "0 0 0 2px var(--chakra-colors-blue-600), 0 0 8px var(--chakra-colors-blue-600)"
- },
- Rhythmic: {
+ }],
+ ["Rhythmic", {
bg: "teal.500",
border: "0 0 0 2px var(--chakra-colors-green-600), 0 0 8px var(--chakra-colors-green-600)"
- },
- Energetic: {
- bg: "red.600",
- border: "0 0 0 2px var(--chakra-colors-orange-600), 0 0 8px var(--chakra-colors-orange-600)"
- },
- "70 BPM": {
+ }],
+ ["70 BPM", {
bg: "orange.500",
border: "0 0 0 2px var(--chakra-colors-yellow-600), 0 0 8px var(--chakra-colors-yellow-600)"
- },
- "120 BPM": {
+ }],
+ ["120 BPM", {
bg: "green.500",
border: "0 0 0 2px var(--chakra-colors-cyan-600), 0 0 8px var(--chakra-colors-cyan-600)"
- },
- "230 BPM": {
+ }],
+ ["230 BPM", {
bg: "purple.500",
border: "0 0 0 2px var(--chakra-colors-purple-600), 0 0 8px var(--chakra-colors-purple-600)"
- },
- "😌": {
+ }],
+ ["😌", {
bg: "cyan.600",
border: "0 0 0 2px var(--chakra-colors-blue-600), 0 0 8px var(--chakra-colors-blue-600)"
- },
- "🙂": {
+ }],
+ ["🙂", {
bg: "orange.600",
border: "0 0 0 2px var(--chakra-colors-yellow-600), 0 0 8px var(--chakra-colors-yellow-600)"
- },
- "😝": {
+ }],
+ ["😝", {
bg: "pink.500",
border: "0 0 0 2px var(--chakra-colors-orange-500), 0 0 8px var(--chakra-colors-orange-500)"
- }
-};
\ No newline at end of file
+ }],
+]);
\ No newline at end of file
diff --git a/front/src/dynamicCSS/VolumeSliderStyle.tsx b/front/src/dynamicCSS/VolumeSliderStyle.tsx
index dfd1ecf..eed72a5 100644
--- a/front/src/dynamicCSS/VolumeSliderStyle.tsx
+++ b/front/src/dynamicCSS/VolumeSliderStyle.tsx
@@ -1,11 +1,13 @@
+import { IconType } from "react-icons/lib";
import { PiSpeakerHighBold, PiSpeakerSlashBold, PiSpeakerLowBold } from "react-icons/pi";
-const VolumeStyle = {
- threshold: 0.5,
- Icon: [PiSpeakerSlashBold, PiSpeakerLowBold, PiSpeakerHighBold ]
-};
+const MapVolumeStyle = new Map ([
+ [0, PiSpeakerSlashBold],
+ [1, PiSpeakerLowBold],
+ [2, PiSpeakerHighBold]
+]);
export const getVolumeIconStyle = (value: number) => {
- const idx = value === 0 ? "0" : value < 0.5 ? "1" : "2";
- return VolumeStyle.Icon[idx]
+ const idx = value === 0 ? 0 : value < 0.5 ? 1 : 2;
+ return MapVolumeStyle.get(idx) ?? PiSpeakerHighBold;
};
\ No newline at end of file
diff --git a/front/src/dynamicCSS/getDynamicSlider.tsx b/front/src/dynamicCSS/getDynamicSlider.tsx
index c8420a7..b054982 100644
--- a/front/src/dynamicCSS/getDynamicSlider.tsx
+++ b/front/src/dynamicCSS/getDynamicSlider.tsx
@@ -1,15 +1,39 @@
import { SliderTitles } from "@/types/RecDataTypes";
-import { SliderStyles } from "./SliderStyles";
+import { defaultSliderStyle, MapSliderStyles } from "./SliderStyles";
import { Icon } from "@chakra-ui/react";
export const getSliderStyle = (title: SliderTitles, value: number) => {
- const style = SliderStyles[title];
- const idx = value < style.threshold[0] ? 0 : value < style.threshold[1] ? 1 : 2;
+ const style = MapSliderStyles.get(title) || defaultSliderStyle;
+ const [lowBg, midBg, HighBg] = style.SliderBg;
+ const [lowIcon, midIcon, HighIcon] = style.Icon;
+ const [lowIconCol, midIconCol, HighIconCol] = style.IconColor;
+ const [lowBorder, midBorder, HighBorder] = style.IconBorder;
+ const [lowShadow, midShadow, HighShadow] = style.shadow;
- return {
- Bg: style.SliderBg[idx],
- Icon: ,
- IconBorder: style.IconBorder[idx],
- Shadow: style.shadow[idx]
- };
+ switch (true) {
+ case value < style.threshold[0]: {
+ return {
+ Bg: lowBg,
+ Icon: ,
+ IconBorder: lowBorder,
+ Shadow: lowShadow,
+ }
+ }
+ case value < style.threshold[1]: {
+ return {
+ Bg: midBg,
+ Icon: ,
+ IconBorder: midBorder,
+ Shadow: midShadow,
+ }
+ }
+ default: {
+ return {
+ Bg: HighBg,
+ Icon: ,
+ IconBorder: HighBorder,
+ Shadow: HighShadow,
+ }
+ }
+ }
};
\ No newline at end of file
diff --git a/front/src/types/modalCssTypes.ts b/front/src/types/modalCssTypes.ts
index 10c8722..9dd96f5 100644
--- a/front/src/types/modalCssTypes.ts
+++ b/front/src/types/modalCssTypes.ts
@@ -11,21 +11,14 @@ export interface SwitchStyle {
border: string;
};
-export interface SwitchTagsStyles {
- Vocals: SwitchStyle;
- Instrumental: SwitchStyle;
- Happy: SwitchStyle;
- Sad: SwitchStyle;
- Acoustic: SwitchStyle;
- Electronic: SwitchStyle;
-};
+export type SwitchType = "Vocals" | "Instrumental" | "Happy" | "Sad" | "Acoustic" | "Electronic";
export interface SliderTagsProps {
bg: string;
border: string;
};
-interface SliderStylesProps {
+export interface SliderStylesProps {
threshold: number[];
SliderBg: string[];
Icon: IconType[];
@@ -34,24 +27,4 @@ interface SliderStylesProps {
shadow: string[];
};
-export interface SliderStylesOptions {
- Energy: SliderStylesProps;
- Danceability: SliderStylesProps;
- Tempo: SliderStylesProps;
- Sentiment: SliderStylesProps;
-};
-
-export interface SliderTagsOptions {
- Relaxed: SliderTagsProps;
- Active: SliderTagsProps;
- Intense: SliderTagsProps;
- Calm: SliderTagsProps;
- Rhythmic: SliderTagsProps;
- Energetic: SliderTagsProps;
- "70 BPM": SliderTagsProps;
- "120 BPM": SliderTagsProps;
- "230 BPM": SliderTagsProps;
- "😌": SliderTagsProps;
- "🙂": SliderTagsProps;
- "😝": SliderTagsProps;
-};
\ No newline at end of file
+export type SliderType = "Energy" | "Danceability" | "Tempo" | "Sentiment";
\ No newline at end of file