Compare commits
10 Commits
59ea576181
...
frontend-0
Author | SHA1 | Date | |
---|---|---|---|
2dd79f3c94 | |||
f1d7e971fe | |||
0860b83486 | |||
7149f70cca | |||
6fd64a1681 | |||
bffbfd6ec0 | |||
64a2264c30 | |||
448ec5c647 | |||
|
a540d12ed2 | ||
|
b8e682f333 |
@@ -1,61 +0,0 @@
|
|||||||
name: Deployment Pipeline
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Setup SSH
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
|
||||||
chmod 600 ~/.ssh/id_rsa
|
|
||||||
ssh-keyscan raphix.fr >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Deploy Subsonics
|
|
||||||
run: |
|
|
||||||
ssh -o StrictHostKeyChecking=no raphix@raphix.fr << 'EOF'
|
|
||||||
sudo su - gitlab-ci << 'INNER_EOF'
|
|
||||||
set -e
|
|
||||||
echo "[Subsonics-Deploy] - Stage - Déploiement - START "
|
|
||||||
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Processing"
|
|
||||||
cd /home/gitlab-ci
|
|
||||||
whoami
|
|
||||||
pm2 stop "Subsonics" || true
|
|
||||||
pm2 delete "Subsonics" || true
|
|
||||||
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Success"
|
|
||||||
|
|
||||||
mv /home/gitlab-ci/backend/data/ /home/gitlab-ci/tempdata || true
|
|
||||||
|
|
||||||
echo "[Subsonics-Deploy] - Suppression de Subsonics : Processing"
|
|
||||||
rm -rf ./backend
|
|
||||||
echo "[Subsonics-Deploy] - Suppression de Subsonics : Success"
|
|
||||||
|
|
||||||
echo "[Subsonics-Deploy] - Installation de Subsonics : Processing"
|
|
||||||
git clone https://git.raphix.fr/subsonics/chopin.git
|
|
||||||
echo "[Subsonics-Deploy] - Installation de Subsonics : Success"
|
|
||||||
|
|
||||||
echo "[Subsonics-Deploy] - Installation des dépendances : Processing"
|
|
||||||
cd /home/gitlab-ci/backend
|
|
||||||
rm -r /home/gitlab-ci/backend/data || true
|
|
||||||
mv /home/gitlab-ci/tempdata/ /home/gitlab-ci/backend/data || true
|
|
||||||
npm install --omit=dev
|
|
||||||
echo "[Subsonics-Deploy] - Installation des dépendances : Success"
|
|
||||||
|
|
||||||
echo "[Subsonics-Deploy] - Démarrage de Subsonics : Processing"
|
|
||||||
cd /home/gitlab-ci
|
|
||||||
pm2 start subsonic.config.js
|
|
||||||
echo "[Subsonics-Deploy] - Démarrage de Subsonics : Success"
|
|
||||||
echo "[Subsonics-Deploy] - Stage - Déploiement - END"
|
|
||||||
INNER_EOF
|
|
||||||
EOF
|
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -142,6 +142,3 @@ docs/_book
|
|||||||
test/
|
test/
|
||||||
|
|
||||||
data/
|
data/
|
||||||
|
|
||||||
__DEBUG.js
|
|
||||||
__TEST.js
|
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
12
.idea/chopin.iml
generated
Normal file
12
.idea/chopin.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
17
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
17
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="CssUnknownProperty" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myCustomPropertiesEnabled" value="true" />
|
||||||
|
<option name="myIgnoreVendorSpecificProperties" value="false" />
|
||||||
|
<option name="myCustomPropertiesList">
|
||||||
|
<value>
|
||||||
|
<list size="2">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="text-edge" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="leading-trim" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/chopin.iml" filepath="$PROJECT_DIR$/.idea/chopin.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
25
.idea/watcherTasks.xml
generated
Normal file
25
.idea/watcherTasks.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectTasksOptions">
|
||||||
|
<TaskOptions isEnabled="false">
|
||||||
|
<option name="arguments" value="$FileName$:$FileNameWithoutExtension$.css" />
|
||||||
|
<option name="checkSyntaxErrors" value="true" />
|
||||||
|
<option name="description" />
|
||||||
|
<option name="exitCodeBehavior" value="ERROR" />
|
||||||
|
<option name="fileExtension" value="scss" />
|
||||||
|
<option name="immediateSync" value="true" />
|
||||||
|
<option name="name" value="SCSS" />
|
||||||
|
<option name="output" value="$FileNameWithoutExtension$.css:$FileNameWithoutExtension$.css.map" />
|
||||||
|
<option name="outputFilters">
|
||||||
|
<array />
|
||||||
|
</option>
|
||||||
|
<option name="outputFromStdout" value="false" />
|
||||||
|
<option name="program" value="sass" />
|
||||||
|
<option name="runOnExternalChanges" value="true" />
|
||||||
|
<option name="scopeName" value="Project Files" />
|
||||||
|
<option name="trackOnlyRoot" value="true" />
|
||||||
|
<option name="workingDir" value="$FileDir$" />
|
||||||
|
<envs />
|
||||||
|
</TaskOptions>
|
||||||
|
</component>
|
||||||
|
</project>
|
116
.idea/workspace.xml
generated
Normal file
116
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="5847035d-0a9b-4d26-a129-e3ed88eb8bd7" name="Changes" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/frontend/src/assets/Icons/SaveIcon.vue" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/frontend/src/assets/Icons/UploadIcon.vue" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/frontend/src/components/Header.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/src/components/Header.vue" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/frontend/src/components/LectureList.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/src/components/LectureList.vue" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/frontend/src/items/Miniature.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/src/items/Miniature.vue" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/frontend/src/items/SwitchTab.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/src/items/SwitchTab.vue" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="SCSS File" />
|
||||||
|
<option value="TypeScript File" />
|
||||||
|
<option value="Vue Composition API Component" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectColorInfo">{
|
||||||
|
"associatedIndex": 2
|
||||||
|
}</component>
|
||||||
|
<component name="ProjectId" id="2tXfL9BjUPbyXVFkt7SA8WxNDUk" />
|
||||||
|
<component name="ProjectLevelVcsManager">
|
||||||
|
<ConfirmationsSetting value="2" id="Add" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
|
"keyToString": {
|
||||||
|
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
|
"git-widget-placeholder": "frontend-0.2.0",
|
||||||
|
"last_opened_file_path": "/Users/gabriel/Codes/Project/GitHub/chopin",
|
||||||
|
"list.type.of.created.stylesheet": "SCSS",
|
||||||
|
"node.js.detected.package.eslint": "true",
|
||||||
|
"node.js.detected.package.tslint": "true",
|
||||||
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
|
"nodejs_package_manager_path": "npm",
|
||||||
|
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||||
|
"ts.external.directory.path": "/Users/gabriel/Codes/Project/GitHub/chopin/frontend/node_modules/typescript/lib",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
},
|
||||||
|
"keyToStringList": {
|
||||||
|
"vue.recent.templates": [
|
||||||
|
"Vue Composition API Component"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]]></component>
|
||||||
|
<component name="RecentsManager">
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/frontend/src/assets/Icons" />
|
||||||
|
<recent name="$PROJECT_DIR$/frontend/src/views" />
|
||||||
|
<recent name="$PROJECT_DIR$/frontend/src/items" />
|
||||||
|
<recent name="$PROJECT_DIR$/frontend/src/components" />
|
||||||
|
</key>
|
||||||
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/frontend/src/views" />
|
||||||
|
<recent name="$PROJECT_DIR$/frontend/src/assets/Icons" />
|
||||||
|
</key>
|
||||||
|
</component>
|
||||||
|
<component name="SharedIndexes">
|
||||||
|
<attachedChunks>
|
||||||
|
<set>
|
||||||
|
<option value="bundled-js-predefined-d6986cc7102b-1632447f56bf-JavaScript-WS-243.26053.12" />
|
||||||
|
</set>
|
||||||
|
</attachedChunks>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="5847035d-0a9b-4d26-a129-e3ed88eb8bd7" name="Changes" comment="" />
|
||||||
|
<created>1740503280401</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1740503280401</updated>
|
||||||
|
<workItem from="1740503281735" duration="34792000" />
|
||||||
|
<workItem from="1740931209372" duration="2409000" />
|
||||||
|
<workItem from="1740934406955" duration="5718000" />
|
||||||
|
<workItem from="1740989873081" duration="18670000" />
|
||||||
|
<workItem from="1741365838888" duration="54000" />
|
||||||
|
<workItem from="1741628247325" duration="887000" />
|
||||||
|
<workItem from="1742802910685" duration="950000" />
|
||||||
|
<workItem from="1743000529409" duration="1573000" />
|
||||||
|
<workItem from="1743168539274" duration="1223000" />
|
||||||
|
<workItem from="1743406006503" duration="564000" />
|
||||||
|
<workItem from="1744218861266" duration="19000" />
|
||||||
|
<workItem from="1744793678915" duration="3951000" />
|
||||||
|
<workItem from="1744962250986" duration="599000" />
|
||||||
|
<workItem from="1744963762669" duration="7674000" />
|
||||||
|
<workItem from="1745243053934" duration="15721000" />
|
||||||
|
<workItem from="1746260006352" duration="20086000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@@ -1,13 +0,0 @@
|
|||||||
<div class="changelog-version changelog-actual">
|
|
||||||
<h2>Chopin - Version /*1.0.0*/</h2>
|
|
||||||
<p class="changelog-date">*_Date de sortie_*: *-XX/XX/XXXX-*</p>
|
|
||||||
<ul>
|
|
||||||
<li>[FRONTEND] Sortie de la version 1.0.0 de Chopin, le bot Discord pour SubSonics.</li>
|
|
||||||
<li>[BACKEND] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
|
|
||||||
<li>[DISCORD] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
|
|
||||||
<li>[PLAYER] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
|
|
||||||
<li>[AUTRE] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
|
|
||||||
<li>[FIX] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
|
|
||||||
<li>[AJOUT] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
> Cette version est une refonte complète et intégrale de [Subsonics - Web](https://git.raphix.fr/subsonics/web)
|
> Cette version est une refonte complète et intégrale de [Subsonics - Web](https://git.raphix.fr/subsonics/web)
|
||||||
|
|
||||||
## Bienvenue sur Chopin, la nouvelle version de Subsonics
|
### Bienvenue sur Chopin, la nouvelle version de Subsonics.
|
||||||
|
|
||||||
## **Fonctionnalités**
|
### **Fonctionnalités**
|
||||||
|
|
||||||
> - Lecture de vidéos depuis Youtube, Spotify et SoundClound
|
> - Lecture de vidéos depuis Youtube, Spotify et SoundClound
|
||||||
> - Lecture de fichiers locaux *(Uniquement sur le site)*
|
> - Lecture de fichiers locaux *(Uniquement sur le site)*
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
> - Une interface refaite pour toutes les platformes.
|
> - Une interface refaite pour toutes les platformes.
|
||||||
> - Récupération de vos recommendations & Playlists Spotify / Youtube
|
> - Récupération de vos recommendations & Playlists Spotify / Youtube
|
||||||
|
|
||||||
|
|
||||||
Le FrontEnd est gérée par VueJS ✌️et le BackEnd a été entièrement refait localement pour des réponses plus rapide et plus stable
|
Le FrontEnd est gérée par VueJS ✌️et le BackEnd a été entièrement refait localement pour des réponses plus rapide et plus stable
|
||||||
|
|
||||||
[CHANGELOG](https://git.raphix.fr/subsonics/chopin/src/branch/main/changelog.md)
|
[CHANGELOG](https://git.raphix.fr/subsonics/chopin/src/branch/main/changelog.md)
|
3
TODOS.md
3
TODOS.md
@@ -1,3 +0,0 @@
|
|||||||
# List
|
|
||||||
|
|
||||||
TODO: Récupération des recommendations, playlists Youtube et Spotify
|
|
1717
backend/package-lock.json
generated
Normal file
1717
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
backend/package.json
Normal file
30
backend/package.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "chopin-backend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Discord Bot for music - Fetching everywhere !",
|
||||||
|
"main": "src/main.js",
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ext": "js, html",
|
||||||
|
"ignore": [
|
||||||
|
"*.json",
|
||||||
|
"*.html"
|
||||||
|
],
|
||||||
|
"delay": "2000000"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "nodemon src/main.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "Raphix",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"discord.js": "^14.18.0",
|
||||||
|
"express": "^4.21.2",
|
||||||
|
"loguix": "^1.4.2",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
|
"uuid": "^11.1.0",
|
||||||
|
"webmetrik": "^0.1.4"
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,10 @@ function setMusicActivity(songName, artistName, imageUrl) {
|
|||||||
const client = bot.getClient()
|
const client = bot.getClient()
|
||||||
client.user.setActivity(`${songName} - ${artistName}`,{
|
client.user.setActivity(`${songName} - ${artistName}`,{
|
||||||
type: ActivityType.Listening,
|
type: ActivityType.Listening,
|
||||||
url: imageUrl
|
/*assets: {
|
||||||
|
largeImage: imageUrl,
|
||||||
|
largeText: songName
|
||||||
|
}*/
|
||||||
});
|
});
|
||||||
dlog.log(`Activité mise à jour : ${songName} - ${artistName}`)
|
dlog.log(`Activité mise à jour : ${songName} - ${artistName}`)
|
||||||
}
|
}
|
78
backend/src/discord/Bot.js
Normal file
78
backend/src/discord/Bot.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const { Client, GatewayIntentBits, Collection, ActivityType, REST, Routes } = require("discord.js")
|
||||||
|
const fs = require("node:fs")
|
||||||
|
const path = require("path")
|
||||||
|
const { __glob } = require("../utils/GlobalVars")
|
||||||
|
const { LogType } = require("loguix")
|
||||||
|
const config = require("../utils/Database/Configuration")
|
||||||
|
const metric = require("webmetrik")
|
||||||
|
|
||||||
|
const dlog = new LogType("Discord")
|
||||||
|
|
||||||
|
const client = new Client({
|
||||||
|
intents:[GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers],
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
//Getter for the client
|
||||||
|
function getClient() {
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
client.once('ready', () => {
|
||||||
|
dlog.log("Connexion au Bot Discord réussi ! Connecté en tant que : " + client.user.tag)
|
||||||
|
|
||||||
|
const Activity = require("./Activity")
|
||||||
|
Activity.idleActivity()
|
||||||
|
|
||||||
|
const CommandUpdater = require("./CommandUpdater")
|
||||||
|
CommandUpdater.init()
|
||||||
|
|
||||||
|
const commandManager = client.application.commands;
|
||||||
|
|
||||||
|
if (!commandManager) {
|
||||||
|
dlog.error('Command manager not available.');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
commandManager.set([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
dlog.step.end("d_init")
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("interactionCreate", (interaction) => {
|
||||||
|
|
||||||
|
if(!interaction.isCommand()) return;
|
||||||
|
|
||||||
|
var numberOfCommands = new metric.Metric("numberOfCommands", "Nombre de commandes éxécutées")
|
||||||
|
numberOfCommands.setValue(numberOfCommands.getValue() + 1)
|
||||||
|
|
||||||
|
const command = client.commands.get(interaction.commandName)
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Create a metric to count the number of commands executed by each user
|
||||||
|
const userCommand = new metric.Metric("userCommand_" + interaction.member.user.username, "Nombre de commandes éxécutées par l'utilisateur : " + interaction.member.user.username)
|
||||||
|
userCommand.setValue(userCommand.getValue() + 1)
|
||||||
|
dlog.log(interaction.member.user.username + "-> /" + interaction.commandName)
|
||||||
|
command.execute(client, interaction)
|
||||||
|
} catch(error) {
|
||||||
|
|
||||||
|
dlog.error(interaction.member.user.username + "-> /" + interaction.commandName + " : ERREUR RENCONTRE")
|
||||||
|
dlog.error(error)
|
||||||
|
interaction.reply({content:"Erreur lors de l'éxécution de la commande !", ephemeral: true})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
client.login(config.getToken())
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {init, getClient}
|
||||||
|
|
||||||
|
|
@@ -53,18 +53,10 @@ class Command {
|
|||||||
})
|
})
|
||||||
SlashCommand.addStringOption(option => option.setName(SelOption.name).setDescription(SelOption.description).setRequired(SelOption.required).addChoices(choices))
|
SlashCommand.addStringOption(option => option.setName(SelOption.name).setDescription(SelOption.description).setRequired(SelOption.required).addChoices(choices))
|
||||||
}
|
}
|
||||||
if(SelOption.type === "FILE") {
|
|
||||||
SlashCommand.addAttachmentOption(option => option.setName(SelOption.name).setDescription(SelOption.description).setRequired(SelOption.required))
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {SlashCommandBuilder}
|
|
||||||
* @param {Client} client
|
|
||||||
* @param {Interaction} interaction
|
|
||||||
*/
|
|
||||||
this.data = {data: SlashCommand, async execute(client, interaction) {callback(client, interaction)}}
|
this.data = {data: SlashCommand, async execute(client, interaction) {callback(client, interaction)}}
|
||||||
|
|
||||||
}
|
}
|
@@ -50,6 +50,10 @@ client.commands = new Collection()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dlog.step.end("d_commands_refresh")
|
dlog.step.end("d_commands_refresh")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// And of course, make sure you catch and log any errors!
|
// And of course, make sure you catch and log any errors!
|
@@ -10,8 +10,8 @@ const command = new Command("about", "Affiche des informations sur le bot", (cli
|
|||||||
const minutes = Math.floor((uptime % 3600) / 60);
|
const minutes = Math.floor((uptime % 3600) / 60);
|
||||||
const seconds = Math.floor(uptime % 60);
|
const seconds = Math.floor(uptime % 60);
|
||||||
|
|
||||||
const embed = new Embed(interaction)
|
const embed = new Embed()
|
||||||
embed.setColor(237, 12, 91)
|
embed.setColor(0xb0f542)
|
||||||
embed.setThumbnail("https://cdn.discordapp.com/avatars/" + client.user.id + "/" + client.user.avatar + ".png")
|
embed.setThumbnail("https://cdn.discordapp.com/avatars/" + client.user.id + "/" + client.user.avatar + ".png")
|
||||||
embed.setTitle('Subsonics - Chopin')
|
embed.setTitle('Subsonics - Chopin')
|
||||||
embed.addField('Informations',"")
|
embed.addField('Informations',"")
|
||||||
@@ -20,18 +20,15 @@ const command = new Command("about", "Affiche des informations sur le bot", (cli
|
|||||||
embed.addField("Ping", `${client.ws.ping} ms `, true)
|
embed.addField("Ping", `${client.ws.ping} ms `, true)
|
||||||
embed.addField("Réalisé par", "Raphix - 2025", true)
|
embed.addField("Réalisé par", "Raphix - 2025", true)
|
||||||
embed.addColumn()
|
embed.addColumn()
|
||||||
embed.addField('Versions :',"")
|
embed.addField('Versions',"")
|
||||||
embed.addField('Node.js', process.version,true)
|
embed.addField('Node.js', process.version,true)
|
||||||
embed.addField('Discord.js', packageJson.dependencies["discord.js"].replace("^", ""),true)
|
embed.addField('Discord.js', packageJson.dependencies["discord.js"].replace("^", ""),true)
|
||||||
embed.addColumn()
|
embed.addColumn()
|
||||||
embed.addField('Webmetrik', packageJson.dependencies["webmetrik"].replace("^", ""),true)
|
embed.addField('Webmetrik', packageJson.dependencies["webmetrik"].replace("^", ""),true)
|
||||||
embed.addField('Loguix', packageJson.dependencies["loguix"].replace("^", ""),true)
|
embed.addField('Loguix', packageJson.dependencies["loguix"].replace("^", ""),true)
|
||||||
embed.addColumn()
|
embed.addColumn()
|
||||||
embed.addField('FFmpeg', packageJson.dependencies["ffmpeg-static"].replace("^", ""),true)
|
|
||||||
embed.addField('Ytdl', packageJson.dependencies["@distube/ytdl-core"].replace("^", ""),true)
|
|
||||||
embed.addColumn()
|
|
||||||
|
|
||||||
embed.send()
|
embed.send(interaction)
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
@@ -3,7 +3,7 @@ const { Embed } = require('../Embed');
|
|||||||
|
|
||||||
const command = new Command("help", "Affiche la liste des commandes", (client, interaction) => {
|
const command = new Command("help", "Affiche la liste des commandes", (client, interaction) => {
|
||||||
|
|
||||||
const embed = new Embed(interaction)
|
const embed = new Embed()
|
||||||
embed.setColor(0x03ff2d)
|
embed.setColor(0x03ff2d)
|
||||||
embed.setTitle('Comment assister au concert ?')
|
embed.setTitle('Comment assister au concert ?')
|
||||||
embed.setDescription("**Eh ! Tu as eu ton ticket ? Tant mieux ! Voici la liste des commandes à utiliser dans le salon prévu à cet effet !**")
|
embed.setDescription("**Eh ! Tu as eu ton ticket ? Tant mieux ! Voici la liste des commandes à utiliser dans le salon prévu à cet effet !**")
|
||||||
@@ -18,7 +18,7 @@ const command = new Command("help", "Affiche la liste des commandes", (client, i
|
|||||||
option.choices.forEach(choice => {
|
option.choices.forEach(choice => {
|
||||||
choices.push(choice.name)
|
choices.push(choice.name)
|
||||||
})
|
})
|
||||||
CommandName += " <" + choices.join(" | ") +">"
|
CommandName += " " + choices.join(" | ")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ const command = new Command("help", "Affiche la liste des commandes", (client, i
|
|||||||
})
|
})
|
||||||
embed.addField("La queue et la gestion du redémarrage se fait par le site https://subsonics.raphix.fr/", ":star:" )
|
embed.addField("La queue et la gestion du redémarrage se fait par le site https://subsonics.raphix.fr/", ":star:" )
|
||||||
embed.setThumbnail("https://static.wikia.nocookie.net/codelyoko/images/9/95/Subdigitals.jpg/revision/latest/scale-to-width-down/180?cb=20120105180510&path-prefix=fr");
|
embed.setThumbnail("https://static.wikia.nocookie.net/codelyoko/images/9/95/Subdigitals.jpg/revision/latest/scale-to-width-down/180?cb=20120105180510&path-prefix=fr");
|
||||||
embed.send()
|
embed.send(interaction)
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = {command}
|
module.exports = {command}
|
@@ -5,7 +5,7 @@ const { Report } = require('../ReportSender');
|
|||||||
const command = new Command("report", "Signaler un problème avec le bot", (client, interaction) => {
|
const command = new Command("report", "Signaler un problème avec le bot", (client, interaction) => {
|
||||||
const report = new Report(interaction.user.username, interaction.options.getString("type"), interaction.options.getString("description"))
|
const report = new Report(interaction.user.username, interaction.options.getString("type"), interaction.options.getString("description"))
|
||||||
const result = report.send()
|
const result = report.send()
|
||||||
const embed = new Embed(interaction)
|
const embed = new Embed()
|
||||||
|
|
||||||
|
|
||||||
result.then((res) => {
|
result.then((res) => {
|
||||||
@@ -20,7 +20,7 @@ const command = new Command("report", "Signaler un problème avec le bot", (clie
|
|||||||
embed.setDescription("Votre rapport a bien été envoyé !")
|
embed.setDescription("Votre rapport a bien été envoyé !")
|
||||||
|
|
||||||
}
|
}
|
||||||
embed.send()
|
embed.send(interaction)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
14
backend/src/discord/Commands/Web.js
Normal file
14
backend/src/discord/Commands/Web.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const { Command } = require('../Command');
|
||||||
|
const { Embed } = require('../Embed');
|
||||||
|
|
||||||
|
const command = new Command("web", "Affiche le lien vers le site web pour contrôler le bot", (client, interaction) => {
|
||||||
|
const embed = new Embed()
|
||||||
|
embed.setColor(0xffffff)
|
||||||
|
embed.setTitle('Subsonics - Chopin')
|
||||||
|
embed.addBotPicture(client)
|
||||||
|
embed.addField('Lien',"https://subsonics.raphix.fr/")
|
||||||
|
embed.send(interaction)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = {command}
|
@@ -1,20 +1,10 @@
|
|||||||
const { EmbedBuilder, ActionRowBuilder } = require("discord.js");
|
const { EmbedBuilder } = require("discord.js");
|
||||||
|
|
||||||
class Embed {
|
class Embed {
|
||||||
fields;
|
fields;
|
||||||
buttons;
|
constructor() {
|
||||||
constructor (interaction, ephemeral) {
|
|
||||||
this.embed = new EmbedBuilder().setTimestamp()
|
this.embed = new EmbedBuilder().setTimestamp()
|
||||||
this.fields = []
|
this.fields = []
|
||||||
this.buttons = []
|
|
||||||
this.isSended = false
|
|
||||||
if(interaction) {
|
|
||||||
interaction.deferReply({ ephemeral: ephemeral }).then(() => {
|
|
||||||
this.isSended = true
|
|
||||||
})
|
|
||||||
this.interaction = interaction
|
|
||||||
this.ephemeral = ephemeral
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle(title) {
|
setTitle(title) {
|
||||||
@@ -85,48 +75,15 @@ class Embed {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addButton(button) {
|
|
||||||
this.buttons.push(button)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
//Add Fields to an object
|
//Add Fields to an object
|
||||||
this.embed.addFields(this.fields)
|
this.embed.addFields(this.fields)
|
||||||
if(this.buttons.length > 0) {
|
|
||||||
this.actionRow = new ActionRowBuilder()
|
|
||||||
.addComponents(this.buttons);
|
|
||||||
}
|
|
||||||
return this.embed
|
return this.embed
|
||||||
}
|
}
|
||||||
|
|
||||||
async send() {
|
send(interaction) {
|
||||||
// Add a secutiry check to avoid sending an embed if the interaction is not defined and retry one again
|
interaction.reply({embeds: [this.build()]})
|
||||||
while(!this.isSended) {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
}
|
|
||||||
if(this.ephemeral === undefined) this.ephemeral = false;
|
|
||||||
this.interaction.editReply({ embeds: [this.build()], components: this.buttons.length > 0 ? [this.actionRow] : [] })
|
|
||||||
}
|
|
||||||
|
|
||||||
async returnError(message) {
|
|
||||||
this.setColor(150, 20, 20)
|
|
||||||
this.setTitle('Erreur')
|
|
||||||
this.setThumbnail("https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Dialog-error-round.svg/2048px-Dialog-error-round.svg.png")
|
|
||||||
this.setDescription(message)
|
|
||||||
await this.send()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EmbedError extends Embed {
|
module.exports = {Embed}
|
||||||
constructor(message, interaction, ephemeral) {
|
|
||||||
super(interaction, ephemeral)
|
|
||||||
this.setColor(150, 20, 20)
|
|
||||||
this.setTitle('Erreur')
|
|
||||||
this.setThumbnail("https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Dialog-error-round.svg/2048px-Dialog-error-round.svg.png")
|
|
||||||
this.setDescription(message)
|
|
||||||
this.send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {Embed, EmbedError}
|
|
@@ -46,7 +46,6 @@ class Report {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {Report}
|
module.exports = {Report}
|
@@ -5,20 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const { LogType } = require('loguix');
|
||||||
const { __glob } = require("./utils/GlobalVars")
|
const { __glob } = require("./utils/GlobalVars")
|
||||||
require("loguix").setup(__glob.LOGS, __glob.PACKAGEINFO)
|
require("loguix").setup(__glob.LOGS, __glob.PACKAGEINFO)
|
||||||
const config = require("./utils/Database/Configuration")
|
const config = require("./utils/Database/Configuration")
|
||||||
const metric = require("webmetrik")
|
const metric = require("webmetrik")
|
||||||
metric.setMetricFile(__glob.METRIC_FILE)
|
metric.setMetricFile(__glob.METRIC_FILE)
|
||||||
metric.publishMetrics("8001", "subsonicsMetricsRaph")
|
metric.publishMetrics("8001", "raphraph")
|
||||||
|
|
||||||
|
|
||||||
// SETUP
|
// SETUP
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
async function setup() {
|
function setup() {
|
||||||
const DiscordBot = require("./discord/Bot")
|
const DiscordBot = require("./discord/Bot")
|
||||||
await DiscordBot.init()
|
DiscordBot.init()
|
||||||
const Server = require("./server/Server")
|
|
||||||
await Server.init()
|
|
||||||
}
|
}
|
33
backend/src/utils/Database/Configuration.js
Normal file
33
backend/src/utils/Database/Configuration.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const {Database} = require("./Database")
|
||||||
|
const {__glob} = require("../GlobalVars")
|
||||||
|
const {LogType} = require("loguix")
|
||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
const clog = new LogType("Configuration")
|
||||||
|
|
||||||
|
const config = new Database("config", __glob.DATA + path.sep + "config.json", {
|
||||||
|
token: "",
|
||||||
|
report: {
|
||||||
|
channel : "",
|
||||||
|
contact : ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function getToken() {
|
||||||
|
return config.data.token
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReportChannel() {
|
||||||
|
return config.data.report.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReportContact() {
|
||||||
|
return config.data.report.contact
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getToken() == "") {
|
||||||
|
clog.error("Impossible de démarrer sans token valide")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {getToken, getReportChannel, getReportContact}
|
@@ -62,9 +62,6 @@ class Database {
|
|||||||
clog.error(e)
|
clog.error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assure that the database is up to date and reloaded
|
|
||||||
this.update()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@@ -83,10 +80,6 @@ class Database {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getData() {
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
backend/src/utils/GlobalVars.js
Normal file
14
backend/src/utils/GlobalVars.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const path = require("path")
|
||||||
|
const root = path.resolve(__dirname, '../../')
|
||||||
|
|
||||||
|
const __glob = {
|
||||||
|
PACKAGEINFO: root + path.sep + "package.json",
|
||||||
|
ROOT: root + + path.sep,
|
||||||
|
SRC: root + path.sep + "src",
|
||||||
|
LOGS: root + path.sep + "logs",
|
||||||
|
DATA: root + path.sep + "data",
|
||||||
|
COMMANDS: root + path.sep + "src" + path.sep + "discord" + path.sep + "commands",
|
||||||
|
METRIC_FILE: root + path.sep + "data" + path.sep + "metrics.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {__glob}
|
17
changelog.md
Normal file
17
changelog.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# **Changelog**
|
||||||
|
|
||||||
|
- Express JS
|
||||||
|
- Vue JS
|
||||||
|
- Discord JS
|
||||||
|
- Player Youtube
|
||||||
|
|
||||||
|
|
||||||
|
## **Légende de version** :
|
||||||
|
|
||||||
|
* Version X.Y.Z
|
||||||
|
> **X** : Indique une version de travail (Période d'activité) \
|
||||||
|
> **Y** : Indique l'ajout d'une fonctionnalité \
|
||||||
|
> **Z** : Indique la modification ou la réparation d'une fonctionnalité
|
||||||
|
* Tags
|
||||||
|
> **-alpha** : Indique une version de dévelopement inutilisable \
|
||||||
|
> **-rcX** : Indique une sous-version qui ne modifie rien mais qui peux corriger un bug de facon très superficiel
|
24
frontend/.gitignore
vendored
Normal file
24
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
25
frontend/.vite/deps/_metadata.json
Normal file
25
frontend/.vite/deps/_metadata.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"hash": "e3fc6723",
|
||||||
|
"configHash": "0efc9154",
|
||||||
|
"lockfileHash": "61d6d597",
|
||||||
|
"browserHash": "9c288342",
|
||||||
|
"optimized": {
|
||||||
|
"vue": {
|
||||||
|
"src": "../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||||
|
"file": "vue.js",
|
||||||
|
"fileHash": "6c0b41d9",
|
||||||
|
"needsInterop": false
|
||||||
|
},
|
||||||
|
"pinia": {
|
||||||
|
"src": "../../node_modules/pinia/dist/pinia.mjs",
|
||||||
|
"file": "pinia.js",
|
||||||
|
"fileHash": "a962c13a",
|
||||||
|
"needsInterop": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chunks": {
|
||||||
|
"chunk-IJV5NOMV": {
|
||||||
|
"file": "chunk-IJV5NOMV.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12542
frontend/.vite/deps/chunk-IJV5NOMV.js
Normal file
12542
frontend/.vite/deps/chunk-IJV5NOMV.js
Normal file
File diff suppressed because it is too large
Load Diff
7
frontend/.vite/deps/chunk-IJV5NOMV.js.map
Normal file
7
frontend/.vite/deps/chunk-IJV5NOMV.js.map
Normal file
File diff suppressed because one or more lines are too long
3
frontend/.vite/deps/package.json
Normal file
3
frontend/.vite/deps/package.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
5876
frontend/.vite/deps/pinia.js
Normal file
5876
frontend/.vite/deps/pinia.js
Normal file
File diff suppressed because it is too large
Load Diff
7
frontend/.vite/deps/pinia.js.map
Normal file
7
frontend/.vite/deps/pinia.js.map
Normal file
File diff suppressed because one or more lines are too long
342
frontend/.vite/deps/vue.js
Normal file
342
frontend/.vite/deps/vue.js
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
import {
|
||||||
|
BaseTransition,
|
||||||
|
BaseTransitionPropsValidators,
|
||||||
|
Comment,
|
||||||
|
DeprecationTypes,
|
||||||
|
EffectScope,
|
||||||
|
ErrorCodes,
|
||||||
|
ErrorTypeStrings,
|
||||||
|
Fragment,
|
||||||
|
KeepAlive,
|
||||||
|
ReactiveEffect,
|
||||||
|
Static,
|
||||||
|
Suspense,
|
||||||
|
Teleport,
|
||||||
|
Text,
|
||||||
|
TrackOpTypes,
|
||||||
|
Transition,
|
||||||
|
TransitionGroup,
|
||||||
|
TriggerOpTypes,
|
||||||
|
VueElement,
|
||||||
|
assertNumber,
|
||||||
|
callWithAsyncErrorHandling,
|
||||||
|
callWithErrorHandling,
|
||||||
|
camelize,
|
||||||
|
capitalize,
|
||||||
|
cloneVNode,
|
||||||
|
compatUtils,
|
||||||
|
compile,
|
||||||
|
computed,
|
||||||
|
createApp,
|
||||||
|
createBaseVNode,
|
||||||
|
createBlock,
|
||||||
|
createCommentVNode,
|
||||||
|
createElementBlock,
|
||||||
|
createHydrationRenderer,
|
||||||
|
createPropsRestProxy,
|
||||||
|
createRenderer,
|
||||||
|
createSSRApp,
|
||||||
|
createSlots,
|
||||||
|
createStaticVNode,
|
||||||
|
createTextVNode,
|
||||||
|
createVNode,
|
||||||
|
customRef,
|
||||||
|
defineAsyncComponent,
|
||||||
|
defineComponent,
|
||||||
|
defineCustomElement,
|
||||||
|
defineEmits,
|
||||||
|
defineExpose,
|
||||||
|
defineModel,
|
||||||
|
defineOptions,
|
||||||
|
defineProps,
|
||||||
|
defineSSRCustomElement,
|
||||||
|
defineSlots,
|
||||||
|
devtools,
|
||||||
|
effect,
|
||||||
|
effectScope,
|
||||||
|
getCurrentInstance,
|
||||||
|
getCurrentScope,
|
||||||
|
getCurrentWatcher,
|
||||||
|
getTransitionRawChildren,
|
||||||
|
guardReactiveProps,
|
||||||
|
h,
|
||||||
|
handleError,
|
||||||
|
hasInjectionContext,
|
||||||
|
hydrate,
|
||||||
|
hydrateOnIdle,
|
||||||
|
hydrateOnInteraction,
|
||||||
|
hydrateOnMediaQuery,
|
||||||
|
hydrateOnVisible,
|
||||||
|
initCustomFormatter,
|
||||||
|
initDirectivesForSSR,
|
||||||
|
inject,
|
||||||
|
isMemoSame,
|
||||||
|
isProxy,
|
||||||
|
isReactive,
|
||||||
|
isReadonly,
|
||||||
|
isRef,
|
||||||
|
isRuntimeOnly,
|
||||||
|
isShallow,
|
||||||
|
isVNode,
|
||||||
|
markRaw,
|
||||||
|
mergeDefaults,
|
||||||
|
mergeModels,
|
||||||
|
mergeProps,
|
||||||
|
nextTick,
|
||||||
|
normalizeClass,
|
||||||
|
normalizeProps,
|
||||||
|
normalizeStyle,
|
||||||
|
onActivated,
|
||||||
|
onBeforeMount,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onBeforeUpdate,
|
||||||
|
onDeactivated,
|
||||||
|
onErrorCaptured,
|
||||||
|
onMounted,
|
||||||
|
onRenderTracked,
|
||||||
|
onRenderTriggered,
|
||||||
|
onScopeDispose,
|
||||||
|
onServerPrefetch,
|
||||||
|
onUnmounted,
|
||||||
|
onUpdated,
|
||||||
|
onWatcherCleanup,
|
||||||
|
openBlock,
|
||||||
|
popScopeId,
|
||||||
|
provide,
|
||||||
|
proxyRefs,
|
||||||
|
pushScopeId,
|
||||||
|
queuePostFlushCb,
|
||||||
|
reactive,
|
||||||
|
readonly,
|
||||||
|
ref,
|
||||||
|
registerRuntimeCompiler,
|
||||||
|
render,
|
||||||
|
renderList,
|
||||||
|
renderSlot,
|
||||||
|
resolveComponent,
|
||||||
|
resolveDirective,
|
||||||
|
resolveDynamicComponent,
|
||||||
|
resolveFilter,
|
||||||
|
resolveTransitionHooks,
|
||||||
|
setBlockTracking,
|
||||||
|
setDevtoolsHook,
|
||||||
|
setTransitionHooks,
|
||||||
|
shallowReactive,
|
||||||
|
shallowReadonly,
|
||||||
|
shallowRef,
|
||||||
|
ssrContextKey,
|
||||||
|
ssrUtils,
|
||||||
|
stop,
|
||||||
|
toDisplayString,
|
||||||
|
toHandlerKey,
|
||||||
|
toHandlers,
|
||||||
|
toRaw,
|
||||||
|
toRef,
|
||||||
|
toRefs,
|
||||||
|
toValue,
|
||||||
|
transformVNodeArgs,
|
||||||
|
triggerRef,
|
||||||
|
unref,
|
||||||
|
useAttrs,
|
||||||
|
useCssModule,
|
||||||
|
useCssVars,
|
||||||
|
useHost,
|
||||||
|
useId,
|
||||||
|
useModel,
|
||||||
|
useSSRContext,
|
||||||
|
useShadowRoot,
|
||||||
|
useSlots,
|
||||||
|
useTemplateRef,
|
||||||
|
useTransitionState,
|
||||||
|
vModelCheckbox,
|
||||||
|
vModelDynamic,
|
||||||
|
vModelRadio,
|
||||||
|
vModelSelect,
|
||||||
|
vModelText,
|
||||||
|
vShow,
|
||||||
|
version,
|
||||||
|
warn,
|
||||||
|
watch,
|
||||||
|
watchEffect,
|
||||||
|
watchPostEffect,
|
||||||
|
watchSyncEffect,
|
||||||
|
withAsyncContext,
|
||||||
|
withCtx,
|
||||||
|
withDefaults,
|
||||||
|
withDirectives,
|
||||||
|
withKeys,
|
||||||
|
withMemo,
|
||||||
|
withModifiers,
|
||||||
|
withScopeId
|
||||||
|
} from "./chunk-IJV5NOMV.js";
|
||||||
|
export {
|
||||||
|
BaseTransition,
|
||||||
|
BaseTransitionPropsValidators,
|
||||||
|
Comment,
|
||||||
|
DeprecationTypes,
|
||||||
|
EffectScope,
|
||||||
|
ErrorCodes,
|
||||||
|
ErrorTypeStrings,
|
||||||
|
Fragment,
|
||||||
|
KeepAlive,
|
||||||
|
ReactiveEffect,
|
||||||
|
Static,
|
||||||
|
Suspense,
|
||||||
|
Teleport,
|
||||||
|
Text,
|
||||||
|
TrackOpTypes,
|
||||||
|
Transition,
|
||||||
|
TransitionGroup,
|
||||||
|
TriggerOpTypes,
|
||||||
|
VueElement,
|
||||||
|
assertNumber,
|
||||||
|
callWithAsyncErrorHandling,
|
||||||
|
callWithErrorHandling,
|
||||||
|
camelize,
|
||||||
|
capitalize,
|
||||||
|
cloneVNode,
|
||||||
|
compatUtils,
|
||||||
|
compile,
|
||||||
|
computed,
|
||||||
|
createApp,
|
||||||
|
createBlock,
|
||||||
|
createCommentVNode,
|
||||||
|
createElementBlock,
|
||||||
|
createBaseVNode as createElementVNode,
|
||||||
|
createHydrationRenderer,
|
||||||
|
createPropsRestProxy,
|
||||||
|
createRenderer,
|
||||||
|
createSSRApp,
|
||||||
|
createSlots,
|
||||||
|
createStaticVNode,
|
||||||
|
createTextVNode,
|
||||||
|
createVNode,
|
||||||
|
customRef,
|
||||||
|
defineAsyncComponent,
|
||||||
|
defineComponent,
|
||||||
|
defineCustomElement,
|
||||||
|
defineEmits,
|
||||||
|
defineExpose,
|
||||||
|
defineModel,
|
||||||
|
defineOptions,
|
||||||
|
defineProps,
|
||||||
|
defineSSRCustomElement,
|
||||||
|
defineSlots,
|
||||||
|
devtools,
|
||||||
|
effect,
|
||||||
|
effectScope,
|
||||||
|
getCurrentInstance,
|
||||||
|
getCurrentScope,
|
||||||
|
getCurrentWatcher,
|
||||||
|
getTransitionRawChildren,
|
||||||
|
guardReactiveProps,
|
||||||
|
h,
|
||||||
|
handleError,
|
||||||
|
hasInjectionContext,
|
||||||
|
hydrate,
|
||||||
|
hydrateOnIdle,
|
||||||
|
hydrateOnInteraction,
|
||||||
|
hydrateOnMediaQuery,
|
||||||
|
hydrateOnVisible,
|
||||||
|
initCustomFormatter,
|
||||||
|
initDirectivesForSSR,
|
||||||
|
inject,
|
||||||
|
isMemoSame,
|
||||||
|
isProxy,
|
||||||
|
isReactive,
|
||||||
|
isReadonly,
|
||||||
|
isRef,
|
||||||
|
isRuntimeOnly,
|
||||||
|
isShallow,
|
||||||
|
isVNode,
|
||||||
|
markRaw,
|
||||||
|
mergeDefaults,
|
||||||
|
mergeModels,
|
||||||
|
mergeProps,
|
||||||
|
nextTick,
|
||||||
|
normalizeClass,
|
||||||
|
normalizeProps,
|
||||||
|
normalizeStyle,
|
||||||
|
onActivated,
|
||||||
|
onBeforeMount,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onBeforeUpdate,
|
||||||
|
onDeactivated,
|
||||||
|
onErrorCaptured,
|
||||||
|
onMounted,
|
||||||
|
onRenderTracked,
|
||||||
|
onRenderTriggered,
|
||||||
|
onScopeDispose,
|
||||||
|
onServerPrefetch,
|
||||||
|
onUnmounted,
|
||||||
|
onUpdated,
|
||||||
|
onWatcherCleanup,
|
||||||
|
openBlock,
|
||||||
|
popScopeId,
|
||||||
|
provide,
|
||||||
|
proxyRefs,
|
||||||
|
pushScopeId,
|
||||||
|
queuePostFlushCb,
|
||||||
|
reactive,
|
||||||
|
readonly,
|
||||||
|
ref,
|
||||||
|
registerRuntimeCompiler,
|
||||||
|
render,
|
||||||
|
renderList,
|
||||||
|
renderSlot,
|
||||||
|
resolveComponent,
|
||||||
|
resolveDirective,
|
||||||
|
resolveDynamicComponent,
|
||||||
|
resolveFilter,
|
||||||
|
resolveTransitionHooks,
|
||||||
|
setBlockTracking,
|
||||||
|
setDevtoolsHook,
|
||||||
|
setTransitionHooks,
|
||||||
|
shallowReactive,
|
||||||
|
shallowReadonly,
|
||||||
|
shallowRef,
|
||||||
|
ssrContextKey,
|
||||||
|
ssrUtils,
|
||||||
|
stop,
|
||||||
|
toDisplayString,
|
||||||
|
toHandlerKey,
|
||||||
|
toHandlers,
|
||||||
|
toRaw,
|
||||||
|
toRef,
|
||||||
|
toRefs,
|
||||||
|
toValue,
|
||||||
|
transformVNodeArgs,
|
||||||
|
triggerRef,
|
||||||
|
unref,
|
||||||
|
useAttrs,
|
||||||
|
useCssModule,
|
||||||
|
useCssVars,
|
||||||
|
useHost,
|
||||||
|
useId,
|
||||||
|
useModel,
|
||||||
|
useSSRContext,
|
||||||
|
useShadowRoot,
|
||||||
|
useSlots,
|
||||||
|
useTemplateRef,
|
||||||
|
useTransitionState,
|
||||||
|
vModelCheckbox,
|
||||||
|
vModelDynamic,
|
||||||
|
vModelRadio,
|
||||||
|
vModelSelect,
|
||||||
|
vModelText,
|
||||||
|
vShow,
|
||||||
|
version,
|
||||||
|
warn,
|
||||||
|
watch,
|
||||||
|
watchEffect,
|
||||||
|
watchPostEffect,
|
||||||
|
watchSyncEffect,
|
||||||
|
withAsyncContext,
|
||||||
|
withCtx,
|
||||||
|
withDefaults,
|
||||||
|
withDirectives,
|
||||||
|
withKeys,
|
||||||
|
withMemo,
|
||||||
|
withModifiers,
|
||||||
|
withScopeId
|
||||||
|
};
|
7
frontend/.vite/deps/vue.js.map
Normal file
7
frontend/.vite/deps/vue.js.map
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"sources": [],
|
||||||
|
"sourcesContent": [],
|
||||||
|
"mappings": "",
|
||||||
|
"names": []
|
||||||
|
}
|
3
frontend/.vscode/extensions.json
vendored
Normal file
3
frontend/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
5
frontend/README.md
Normal file
5
frontend/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Vue 3 + TypeScript + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
13
frontend/index.html
Normal file
13
frontend/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Vue + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
2089
frontend/package-lock.json
generated
Normal file
2089
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
frontend/package.json
Normal file
25
frontend/package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pinia": "^3.0.1",
|
||||||
|
"scss": "^0.2.4",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"sass-embedded": "^1.85.1",
|
||||||
|
"typescript": "~5.7.2",
|
||||||
|
"vite": "^6.2.0",
|
||||||
|
"vue-tsc": "^2.2.4"
|
||||||
|
}
|
||||||
|
}
|
1
frontend/public/vite.svg
Normal file
1
frontend/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
15
frontend/src/App.vue
Normal file
15
frontend/src/App.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const savedTheme = localStorage.getItem("theme");
|
||||||
|
document.documentElement.setAttribute("data-theme", savedTheme || "dark");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
179
frontend/src/assets/Global.scss
Normal file
179
frontend/src/assets/Global.scss
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
[data-theme='light'] {
|
||||||
|
--neutral-50: #F6F7F6;
|
||||||
|
--neutral-100: #E9EAE8;
|
||||||
|
--neutral-200: #C9CBC4;
|
||||||
|
--neutral-300: #A6A99F;
|
||||||
|
--neutral-400: #81867B;
|
||||||
|
--neutral-500: #686C60;
|
||||||
|
--neutral-600: #51554C;
|
||||||
|
--neutral-700: #42463F;
|
||||||
|
--neutral-800: #373A35;
|
||||||
|
--neutral-900: #2A2B28;
|
||||||
|
--neutral-950: #111210;
|
||||||
|
|
||||||
|
|
||||||
|
--primary-50: #FFF0F3;
|
||||||
|
--primary-100: #FFE2E8;
|
||||||
|
--primary-200: #FFC9D7;
|
||||||
|
--primary-300: #FF9CB6;
|
||||||
|
--primary-400: #FF6591;
|
||||||
|
--primary-500: #ED0C5B;
|
||||||
|
--primary-500: #e10954;
|
||||||
|
--primary-700: #CD034F;
|
||||||
|
--primary-800: #AB064A;
|
||||||
|
--primary-900: #920945;
|
||||||
|
--primary-950: #520021;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] {
|
||||||
|
--neutral-50: #111210;
|
||||||
|
--neutral-100: #2A2B28;
|
||||||
|
--neutral-200: #373A35;
|
||||||
|
--neutral-300: #42463F;
|
||||||
|
--neutral-400: #51554C;
|
||||||
|
--neutral-500: #686C60;
|
||||||
|
--neutral-600: #81867B;
|
||||||
|
--neutral-700: #A6A99F;
|
||||||
|
--neutral-800: #C9CBC4;
|
||||||
|
--neutral-900: #E9EAE8;
|
||||||
|
--neutral-950: #F6F7F6;
|
||||||
|
|
||||||
|
--primary-50: #520021;
|
||||||
|
--primary-100: #920945;
|
||||||
|
--primary-200: #AB064A;
|
||||||
|
--primary-300: #CD034F;
|
||||||
|
--primary-400: #e10954;
|
||||||
|
--primary-500: #ED0C5B;
|
||||||
|
--primary-600: #FF6591;
|
||||||
|
--primary-700: #FF9CB6;
|
||||||
|
--primary-800: #FFC9D7;
|
||||||
|
--primary-900: #FFE2E8;
|
||||||
|
--primary-950: #FFF0F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* width */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
|
border-radius: 900px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track */
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle */
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--primary-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle on hover */
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--primary-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
body, p, html, button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
background: var(--neutral-50);
|
||||||
|
color: var(--neutral-950);
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: var(--neutral-900);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 29px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-text {
|
||||||
|
color: var(--neutral-50);
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.iconDefault {
|
||||||
|
color: var(--neutral-950);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconDefaultStroke {
|
||||||
|
Stroke: var(--neutral-950);
|
||||||
|
}
|
||||||
|
.iconDefaultStrokeReverse {
|
||||||
|
stroke: var(--neutral-50);
|
||||||
|
}
|
||||||
|
.iconFixedStrokeWhite {
|
||||||
|
stroke: #F6F7F6;
|
||||||
|
}
|
||||||
|
.iconFixedStrokeBlack {
|
||||||
|
stroke: #111210;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconDefaultFill {
|
||||||
|
fill: var(--neutral-950);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconDefaultFillReverse {
|
||||||
|
fill: var(--neutral-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconFixedFillWhite {
|
||||||
|
fill: #F6F7F6;
|
||||||
|
}
|
||||||
|
.iconFixedFillBlack {
|
||||||
|
fill: #111210;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorFixedWhite {
|
||||||
|
color: #F6F7F6;
|
||||||
|
}
|
||||||
|
.colorFixedBlack {
|
||||||
|
color: #111210;
|
||||||
|
}
|
14
frontend/src/assets/Icons/Add.vue
Normal file
14
frontend/src/assets/Icons/Add.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 5V19M5 12H19" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/DarkModeIcone.vue
Normal file
15
frontend/src/assets/Icons/DarkModeIcone.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.8933 19.7197L18.0903 18.996L17.8933 19.7197ZM17.9409 20.0936L18.2697 20.7677L17.9409 20.0936ZM17.9409 3.9064L18.2697 3.23233L17.9409 3.9064ZM12.75 12C12.75 8.65778 15.0122 5.84201 18.0903 5.00396L17.6963 3.55664C13.982 4.56791 11.25 7.96395 11.25 12H12.75ZM18.0903 18.996C15.0122 18.158 12.75 15.3422 12.75 12H11.25C11.25 16.0361 13.982 19.4321 17.6963 20.4434L18.0903 18.996ZM17.6121 19.4195C16.522 19.9513 15.297 20.25 14 20.25V21.75C15.5298 21.75 16.9793 21.3971 18.2697 20.7677L17.6121 19.4195ZM14 20.25C9.44367 20.25 5.75002 16.5563 5.75002 12H4.25002C4.25002 17.3848 8.61525 21.75 14 21.75V20.25ZM5.75002 12C5.75002 7.44365 9.44367 3.75 14 3.75V2.25C8.61525 2.25 4.25002 6.61522 4.25002 12H5.75002ZM14 3.75C15.297 3.75 16.522 4.04874 17.6121 4.58047L18.2697 3.23233C16.9793 2.60286 15.5298 2.25 14 2.25V3.75ZM17.6963 20.4434C17.4677 20.3811 17.3292 20.1871 17.3033 19.9942C17.2762 19.7923 17.3692 19.538 17.6121 19.4195L18.2697 20.7677C18.6791 20.568 18.8378 20.1511 18.79 19.7947C18.7433 19.4473 18.4977 19.107 18.0903 18.996L17.6963 20.4434ZM18.0903 5.00396C18.4977 4.89304 18.7433 4.55271 18.79 4.20534C18.8378 3.84892 18.6791 3.43204 18.2697 3.23233L17.6121 4.58047C17.3692 4.46203 17.2762 4.20775 17.3033 4.00579C17.3292 3.81289 17.4677 3.61887 17.6963 3.55664L18.0903 5.00396Z"
|
||||||
|
class="iconDefaultFill"/>
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/DefaultPlaylist.vue
Normal file
15
frontend/src/assets/Icons/DefaultPlaylist.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="1" y="1" width="68" height="68" rx="14" class="iconDefaultStroke" stroke-width="2"/>
|
||||||
|
<path d="M32 41V29.3554C32 28.8738 32 28.6331 32.0876 28.4378C32.1648 28.2656 32.2892 28.1189 32.4463 28.0144C32.6245 27.8959 32.862 27.8563 33.337 27.7771L42.137 26.3105C42.7779 26.2036 43.0984 26.1502 43.3482 26.243C43.5674 26.3244 43.7511 26.48 43.8674 26.6829C44 26.914 44 27.2389 44 27.8887V39M32 41C32 42.6568 30.6568 44 29 44C27.3432 44 26 42.6568 26 41C26 39.3431 27.3432 38 29 38C30.6568 38 32 39.3431 32 41ZM44 39C44 40.6568 42.6569 42 41 42C39.3431 42 38 40.6568 38 39C38 37.3431 39.3431 36 41 36C42.6569 36 44 37.3431 44 39Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
22
frontend/src/assets/Icons/DiscordLogo.vue
Normal file
22
frontend/src/assets/Icons/DiscordLogo.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="153" height="30" viewBox="0 0 292 57" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M61.7958 4.7294C57.0736 2.52 52.0244 0.9143 46.7456 0C46.0973 1.1721 45.3399 2.7486 44.8177 4.0027C39.2062 3.1588 33.6463 3.1588 28.138 4.0027C27.6159 2.7486 26.8413 1.1721 26.1872 0C20.9027 0.9143 15.8477 2.5259 11.1255 4.7411C1.60078 19.1342 -0.981215 33.1698 0.309785 47.0061C6.62708 51.7237 12.7493 54.5895 18.7682 56.4648C20.2543 54.4195 21.5797 52.2453 22.7215 49.9539C20.5469 49.1276 18.4641 48.1079 16.4961 46.9241C17.0182 46.5373 17.5289 46.1329 18.0223 45.7168C30.0257 51.3311 43.0677 51.3311 54.9277 45.7168C55.4269 46.1329 55.9375 46.5373 56.4539 46.9241C54.4801 48.1137 52.3916 49.1334 50.217 49.9598C51.3588 52.2453 52.6785 54.4254 54.1703 56.4706C60.195 54.5953 66.3229 51.7296 72.6402 47.0061C74.155 30.9663 70.0525 17.0596 61.7958 4.7294ZM24.3568 38.4969C20.7535 38.4969 17.7985 35.133 17.7985 31.0366C17.7985 26.9402 20.6904 23.5705 24.3568 23.5705C28.0233 23.5705 30.9782 26.9343 30.9151 31.0366C30.9208 35.133 28.0233 38.4969 24.3568 38.4969ZM48.5932 38.4969C44.9899 38.4969 42.0349 35.133 42.0349 31.0366C42.0349 26.9402 44.9267 23.5705 48.5932 23.5705C52.2596 23.5705 55.2146 26.9343 55.1515 31.0366C55.1515 35.133 52.2596 38.4969 48.5932 38.4969Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M98.0293 14.406H113.693C117.469 14.406 120.659 15.0096 123.276 16.211C125.886 17.4123 127.843 19.0884 129.14 21.2333C130.436 23.3782 131.09 25.8337 131.09 28.5998C131.09 31.3073 130.413 33.7628 129.059 35.9604C127.705 38.1639 125.645 39.9045 122.874 41.1879C120.103 42.4713 116.671 43.1159 112.569 43.1159H98.0293V14.406ZM112.408 35.8198C114.95 35.8198 116.907 35.1693 118.272 33.8741C119.638 32.5731 120.321 30.8033 120.321 28.5588C120.321 26.4783 119.712 24.8198 118.496 23.5774C117.28 22.335 115.438 21.708 112.976 21.708H108.076V35.8198H112.408Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M154.541 43.0809C152.372 42.5066 150.415 41.6744 148.677 40.5785V33.7688C149.991 34.806 151.752 35.6617 153.961 36.3356C156.17 37.0037 158.305 37.3377 160.37 37.3377C161.334 37.3377 162.063 37.2088 162.556 36.9509C163.05 36.6931 163.297 36.3825 163.297 36.025C163.297 35.6148 163.165 35.2749 162.895 34.9994C162.625 34.724 162.103 34.4954 161.329 34.302L156.509 33.1944C153.749 32.5381 151.792 31.6297 150.628 30.4635C149.463 29.3031 148.883 27.7794 148.883 25.8924C148.883 24.3042 149.388 22.9271 150.41 21.7491C151.425 20.5712 152.871 19.6628 154.747 19.024C156.624 18.3794 158.815 18.0571 161.334 18.0571C163.583 18.0571 165.643 18.3032 167.52 18.7955C169.396 19.2878 170.945 19.9148 172.179 20.6825V27.1231C170.916 26.3554 169.47 25.7518 167.818 25.2946C166.171 24.8434 164.479 24.6207 162.734 24.6207C160.215 24.6207 158.959 25.0602 158.959 25.9334C158.959 26.3437 159.154 26.6484 159.544 26.8535C159.934 27.0586 160.651 27.2696 161.69 27.4923L165.706 28.2307C168.329 28.6937 170.285 29.5083 171.57 30.6686C172.856 31.829 173.498 33.5461 173.498 35.8199C173.498 38.3105 172.437 40.2855 170.308 41.7506C168.179 43.2157 165.161 43.9482 161.248 43.9482C158.947 43.9424 156.71 43.6552 154.541 43.0809Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M182.978 42.2192C180.678 41.0705 178.939 39.5117 177.78 37.5426C176.621 35.5735 176.036 33.3583 176.036 30.8969C176.036 28.4356 176.638 26.2321 177.843 24.2923C179.048 22.3525 180.815 20.8288 183.145 19.7212C185.474 18.6136 188.257 18.0627 191.499 18.0627C195.515 18.0627 198.849 18.9242 201.5 20.6471V28.1543C200.565 27.4979 199.474 26.9646 198.229 26.5544C196.984 26.1442 195.653 25.939 194.23 25.939C191.74 25.939 189.795 26.402 188.389 27.3338C186.983 28.2656 186.278 29.4787 186.278 30.9848C186.278 32.4616 186.96 33.6689 188.326 34.6183C189.692 35.5618 191.671 36.0365 194.27 36.0365C195.607 36.0365 196.927 35.8372 198.229 35.4446C199.526 35.0461 200.645 34.5597 201.58 33.9853V41.2463C198.637 43.0513 195.223 43.9538 191.338 43.9538C188.068 43.9421 185.279 43.3678 182.978 42.2192Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M211.518 42.2195C209.2 41.0709 207.433 39.5003 206.216 37.5019C205 35.5035 204.386 33.2766 204.386 30.8152C204.386 28.3539 204.994 26.1562 206.216 24.2282C207.438 22.3001 209.194 20.7881 211.501 19.6922C213.801 18.5963 216.55 18.0513 219.734 18.0513C222.919 18.0513 225.667 18.5963 227.968 19.6922C230.269 20.7881 232.025 22.2884 233.23 24.2047C234.435 26.1211 235.037 28.3187 235.037 30.8094C235.037 33.2707 234.435 35.4977 233.23 37.496C232.025 39.4944 230.263 41.065 227.945 42.2136C225.627 43.3623 222.89 43.9366 219.729 43.9366C216.567 43.9366 213.83 43.3681 211.518 42.2195ZM223.722 34.9409C224.698 33.9447 225.191 32.6261 225.191 30.9852C225.191 29.3443 224.703 28.0374 223.722 27.0704C222.747 26.0976 221.415 25.6112 219.729 25.6112C218.013 25.6112 216.67 26.0976 215.689 27.0704C214.714 28.0433 214.226 29.3443 214.226 30.9852C214.226 32.6261 214.714 33.9447 215.689 34.9409C216.665 35.9372 218.013 36.4412 219.729 36.4412C221.415 36.4353 222.747 35.9372 223.722 34.9409Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M259.17 19.5748V28.4357C258.149 27.75 256.829 27.4101 255.194 27.4101C253.053 27.4101 251.401 28.0724 250.253 29.3968C249.1 30.7213 248.526 32.7841 248.526 35.5736V43.1218H238.686V19.1236H248.326V26.7538C248.859 23.9642 249.726 21.9073 250.919 20.5769C252.107 19.2525 253.644 18.5903 255.515 18.5903C256.932 18.5903 258.149 18.9185 259.17 19.5748Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M291.864 13.5857V43.122H282.023V37.7481C281.191 39.7699 279.929 41.3112 278.231 42.366C276.532 43.4151 274.432 43.9425 271.942 43.9425C269.716 43.9425 267.777 43.3916 266.118 42.284C264.46 41.1764 263.181 39.6586 262.28 37.7305C261.385 35.8024 260.931 33.6224 260.931 31.1845C260.903 28.6704 261.379 26.4141 262.36 24.4157C263.336 22.4173 264.718 20.8585 266.497 19.7391C268.276 18.6198 270.307 18.0572 272.585 18.0572C277.273 18.0572 280.417 20.1376 282.023 24.2926V13.5857H291.864ZM280.555 34.7769C281.559 33.7806 282.058 32.4855 282.058 30.9032C282.058 29.3736 281.57 28.1253 280.595 27.1701C279.619 26.2149 278.282 25.7343 276.601 25.7343C274.943 25.7343 273.618 26.2207 272.625 27.1935C271.632 28.1664 271.139 29.4263 271.139 30.9852C271.139 32.5441 271.632 33.8158 272.625 34.8003C273.618 35.7848 274.926 36.2771 276.561 36.2771C278.219 36.2713 279.55 35.7731 280.555 34.7769Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M139.382 21.6785C142.091 21.6785 144.288 19.6634 144.288 17.1777C144.288 14.692 142.091 12.677 139.382 12.677C136.672 12.677 134.476 14.692 134.476 17.1777C134.476 19.6634 136.672 21.6785 139.382 21.6785Z" class="iconFixedFillWhite"/>
|
||||||
|
<path d="M134.472 24.7788C137.478 26.1032 141.208 26.1618 144.283 24.7788V43.2507H134.472V24.7788Z" class="iconFixedFillWhite"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/DotsMenu.vue
Normal file
15
frontend/src/assets/Icons/DotsMenu.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/HandIcon.vue
Normal file
14
frontend/src/assets/Icons/HandIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.9 11.4444V14.2222M6.9 11.4444V4.77778C6.9 3.8573 7.66112 3.11111 8.6 3.11111C9.53888 3.11111 10.3 3.8573 10.3 4.77778M6.9 11.4444C6.9 10.524 6.13888 9.77778 5.2 9.77778C4.26112 9.77778 3.5 10.524 3.5 11.4444V13.6667C3.5 18.269 7.30558 22 12 22C16.6944 22 20.5 18.269 20.5 13.6667V8.11111C20.5 7.19064 19.7389 6.44444 18.8 6.44444C17.8611 6.44444 17.1 7.19064 17.1 8.11111M10.3 4.77778V10.8889M10.3 4.77778V3.66667C10.3 2.74619 11.0611 2 12 2C12.9389 2 13.7 2.74619 13.7 3.66667V4.77778M17.1 8.11111V4.77778C17.1 3.8573 16.3389 3.11111 15.4 3.11111C14.4611 3.11111 13.7 3.8573 13.7 4.77778M17.1 8.11111V10.8889M13.7 4.77778V10.8889" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
16
frontend/src/assets/Icons/InPlayListIcon.vue
Normal file
16
frontend/src/assets/Icons/InPlayListIcon.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.5 8.96536C9.5 8.48808 9.5 8.24944 9.59974 8.11621C9.68666 8.0001 9.81971 7.92747 9.96438 7.91713C10.1304 7.90528 10.3311 8.03432 10.7326 8.29242L15.4532 11.3271C15.8016 11.551 15.9758 11.663 16.0359 11.8054C16.0885 11.9298 16.0885 12.0702 16.0359 12.1946C15.9758 12.337 15.8016 12.449 15.4532 12.6729L10.7326 15.7076C10.3311 15.9657 10.1304 16.0948 9.96438 16.0829C9.81971 16.0726 9.68666 15.9999 9.59974 15.8838C9.5 15.7506 9.5 15.512 9.5 15.0347V8.96536Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M3 7.8C3 6.11984 3 5.27976 3.32698 4.63803C3.6146 4.07354 4.07354 3.6146 4.63803 3.32698C5.27976 3 6.11984 3 7.8 3H16.2C17.8802 3 18.7202 3 19.362 3.32698C19.9265 3.6146 20.3854 4.07354 20.673 4.63803C21 5.27976 21 6.11984 21 7.8V16.2C21 17.8802 21 18.7202 20.673 19.362C20.3854 19.9265 19.9265 20.3854 19.362 20.673C18.7202 21 17.8802 21 16.2 21H7.8C6.11984 21 5.27976 21 4.63803 20.673C4.07354 20.3854 3.6146 19.9265 3.32698 19.362C3 18.7202 3 17.8802 3 16.2V7.8Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/LeaveIcon.vue
Normal file
14
frontend/src/assets/Icons/LeaveIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.47542 12.8632C4.44449 11.2623 3.67643 9.54134 3.17124 7.76144C3.01103 7.19699 2.93093 6.91477 2.9297 6.50182C2.92833 6.0436 3.08969 5.42311 3.31412 5.0236C3.51636 4.66357 3.78117 4.39876 4.3108 3.86913L4.46843 3.7115C4.99987 3.18006 5.2656 2.91433 5.55098 2.76999C6.11854 2.48292 6.7888 2.48292 7.35636 2.76999C7.64174 2.91433 7.90747 3.18006 8.43891 3.7115L8.63378 3.90637C8.98338 4.25597 9.15819 4.43078 9.27247 4.60656C9.70347 5.26945 9.70347 6.12403 9.27247 6.78692C9.15819 6.9627 8.98338 7.1375 8.63378 7.4871C8.51947 7.60142 8.46231 7.65857 8.41447 7.72539C8.24446 7.96281 8.18576 8.30707 8.26748 8.58743C8.29048 8.66632 8.32041 8.72867 8.38028 8.85337C8.50111 9.10503 8.62956 9.35395 8.76563 9.59979M11.1817 12.8181L11.2266 12.8632C12.4282 14.0648 13.7869 15.0136 15.2365 15.7096C15.3612 15.7694 15.4235 15.7994 15.5024 15.8224C15.7828 15.9041 16.127 15.8454 16.3644 15.6754C16.4313 15.6275 16.4884 15.5704 16.6027 15.4561C16.9523 15.1064 17.1271 14.9316 17.3029 14.8174C17.9658 14.3864 18.8204 14.3864 19.4833 14.8174C19.6591 14.9316 19.8339 15.1064 20.1835 15.4561L20.3783 15.6509C20.9098 16.1824 21.1755 16.4481 21.3198 16.7335C21.6069 17.301 21.6069 17.9713 21.3198 18.5389C21.1755 18.8242 20.9098 19.09 20.3783 19.6214L20.2207 19.779C19.6911 20.3087 19.4263 20.5735 19.0662 20.7757C18.6667 21.0001 18.0462 21.1615 17.588 21.1601C17.1751 21.1589 16.8928 21.0788 16.3284 20.9186C13.295 20.0576 10.4326 18.4332 8.04466 16.0452L7.99976 16.0001M20.9996 3.00012L2.99961 21.0001" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
13
frontend/src/assets/Icons/LectureListIcon.vue
Normal file
13
frontend/src/assets/Icons/LectureListIcon.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21 12H9M21 6H9M21 18H9M5 12C5 12.5523 4.55228 13 4 13C3.44772 13 3 12.5523 3 12C3 11.4477 3.44772 11 4 11C4.55228 11 5 11.4477 5 12ZM5 6C5 6.55228 4.55228 7 4 7C3.44772 7 3 6.55228 3 6C3 5.44772 3.44772 5 4 5C4.55228 5 5 5.44772 5 6ZM5 18C5 18.5523 4.55228 19 4 19C3.44772 19 3 18.5523 3 18C3 17.4477 3.44772 17 4 17C4.55228 17 5 17.4477 5 18Z" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
21
frontend/src/assets/Icons/LightModeIcone.vue
Normal file
21
frontend/src/assets/Icons/LightModeIcone.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="12" cy="12.007" r="5" class="iconDefaultStroke" stroke-width="1.5"/>
|
||||||
|
<path d="M12 2.68144V4.68144" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M18.5919 18.5957L17.1777 17.1815" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M18.5919 5.41191L17.1777 6.82612" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M21.3224 12.0038L19.3224 12.0038" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M12 19.3262V21.3262" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M6.8223 6.82611L5.40808 5.4119" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M6.8223 17.1815L5.40808 18.5957" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M4.67761 12.0038L2.67761 12.0038" class="iconDefaultStroke" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
30
frontend/src/assets/Icons/LogoIcon.vue
Normal file
30
frontend/src/assets/Icons/LogoIcon.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="48" height="47" viewBox="0 0 48 47" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<mask id="mask0_524_487" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="47">
|
||||||
|
<path d="M47.5 0H0.5V47H47.5V0Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_524_487)">
|
||||||
|
<mask id="mask1_524_487" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-6" y="-7" width="45" height="46">
|
||||||
|
<path d="M6.97291 38.8533L-5.98877 26.1158L26.0299 -6.46667L38.9916 6.27073L6.97291 38.8533ZM18.8607 22.2425L32.0715 8.86645C25.6439 8.66717 13.4832 10.1002 13.5902 17.1495C13.8886 21.0113 18.8607 22.2425 18.8607 22.2425Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask1_524_487)">
|
||||||
|
<path d="M35.1468 9.18089L20.6082 24.0642C20.6082 24.0642 8.48459 22.6094 7.88379 15.958C8.06894 4.26251 30.2957 4.20923 38.2291 6.27751L35.1468 9.18089Z" class="iconDefaultFill"/>
|
||||||
|
</g>
|
||||||
|
<mask id="mask2_524_487" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="6" y="6" width="46" height="46">
|
||||||
|
<path d="M38.9891 6.26685L51.9507 19.0043L19.932 51.5868L6.97034 38.8493L38.9891 6.26685ZM27.1012 22.8774L13.8905 36.2536C20.318 36.4529 32.4788 35.0199 32.3718 27.9707C32.0733 24.1088 27.1012 22.8774 27.1012 22.8774Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask2_524_487)">
|
||||||
|
<path d="M39.0471 7.07983L40.7192 8.90879L28.8153 21.0579C28.8153 21.0579 37.4774 22.511 38.0782 29.1624C37.8931 40.8579 15.6663 40.9111 7.73279 38.8428L39.0471 7.07983Z" fill="#ED0C5B"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/NextIcon.vue
Normal file
15
frontend/src/assets/Icons/NextIcon.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.45308 5.03587C5.79455 4.53629 4.84869 5.00598 4.84869 5.83256V19.7773C4.84869 20.6039 5.79455 21.0736 6.45308 20.574L15.6439 13.6016C16.1714 13.2015 16.1714 12.4084 15.6439 12.0082L6.45308 5.03587ZM2.84869 5.83256C2.84869 3.35282 5.68627 1.94376 7.66185 3.44248L16.8527 10.4149C18.4352 11.6153 18.4352 13.9945 16.8527 15.195L7.66185 22.1674C5.68627 23.6661 2.84869 22.257 2.84869 19.7773V5.83256Z" class="iconDefaultFill"/>
|
||||||
|
<path d="M20.8487 3.80493C20.8487 3.25265 21.2964 2.80493 21.8487 2.80493C22.401 2.80493 22.8487 3.25265 22.8487 3.80493V21.8049C22.8487 22.3572 22.401 22.8049 21.8487 22.8049C21.2964 22.8049 20.8487 22.3572 20.8487 21.8049V3.80493Z" class="iconDefaultFill"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/PauseIcon.vue
Normal file
15
frontend/src/assets/Icons/PauseIcon.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="4.5" y="3.99951" width="6" height="16" rx="1" class="iconFixedStrokeWhite" stroke-width="1.5"/>
|
||||||
|
<rect x="13.5" y="3.99951" width="6" height="16" rx="1" class="iconFixedStrokeWhite" stroke-width="1.5"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/Play.vue
Normal file
14
frontend/src/assets/Icons/Play.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 4.98951C5 4.01835 5 3.53277 5.20249 3.2651C5.37889 3.03191 5.64852 2.88761 5.9404 2.87018C6.27544 2.85017 6.67946 3.11953 7.48752 3.65823L18.0031 10.6686C18.6708 11.1137 19.0046 11.3363 19.1209 11.6168C19.2227 11.8621 19.2227 12.1377 19.1209 12.383C19.0046 12.6635 18.6708 12.886 18.0031 13.3312L7.48752 20.3415C6.67946 20.8802 6.27544 21.1496 5.9404 21.1296C5.64852 21.1122 5.37889 20.9679 5.20249 20.7347C5 20.467 5 19.9814 5 19.0103V4.98951Z" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/PlayIcon.vue
Normal file
14
frontend/src/assets/Icons/PlayIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 4.98951C5 4.01835 5 3.53277 5.20249 3.2651C5.37889 3.03191 5.64852 2.88761 5.9404 2.87018C6.27544 2.85017 6.67946 3.11953 7.48752 3.65823L18.0031 10.6686C18.6708 11.1137 19.0046 11.3363 19.1209 11.6168C19.2227 11.8621 19.2227 12.1377 19.1209 12.383C19.0046 12.6635 18.6708 12.886 18.0031 13.3312L7.48752 20.3415C6.67946 20.8802 6.27544 21.1496 5.9404 21.1296C5.64852 21.1122 5.37889 20.9679 5.20249 20.7347C5 20.467 5 19.9814 5 19.0103V4.98951Z" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
13
frontend/src/assets/Icons/PlaylistIcon.vue
Normal file
13
frontend/src/assets/Icons/PlaylistIcon.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9 18V6.35537C9 5.87383 9 5.63306 9.0876 5.43778C9.16482 5.26565 9.28917 5.11887 9.44627 5.0144C9.62449 4.89588 9.86198 4.8563 10.337 4.77714L19.137 3.31047C19.7779 3.20364 20.0984 3.15023 20.3482 3.243C20.5674 3.32441 20.7511 3.48005 20.8674 3.68286C21 3.91398 21 4.23889 21 4.8887V16M9 18C9 19.6568 7.65685 21 6 21C4.34315 21 3 19.6568 3 18C3 16.3431 4.34315 15 6 15C7.65685 15 9 16.3431 9 18ZM21 16C21 17.6568 19.6569 19 18 19C16.3431 19 15 17.6568 15 16C15 14.3431 16.3431 13 18 13C19.6569 13 21 14.3431 21 16Z" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/PrevIcon.vue
Normal file
15
frontend/src/assets/Icons/PrevIcon.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="29" height="25" viewBox="0 0 29 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.6129 20.574C22.3812 21.0736 23.4847 20.6039 23.4847 19.7773L23.4847 5.83257C23.4847 5.00599 22.3812 4.53631 21.6129 5.03588L10.8902 12.0083C10.2748 12.4084 10.2748 13.2015 10.8902 13.6016L21.6129 20.574ZM25.818 19.7773C25.818 22.2571 22.5075 23.6661 20.2026 22.1674L9.47996 15.195C7.63377 13.9945 7.63377 11.6154 9.47997 10.4149L20.2026 3.4425C22.5075 1.94378 25.818 3.35283 25.818 5.83257L25.818 19.7773Z" class="iconDefaultFill"/>
|
||||||
|
<path d="M2.48465 3.80493C2.48465 3.25265 3.00698 2.80493 3.65132 2.80493C4.29565 2.80493 4.81798 3.25265 4.81798 3.80493V21.8049C4.81798 22.3572 4.29565 22.8049 3.65132 22.8049C3.00698 22.8049 2.48465 22.3572 2.48465 21.8049V3.80493Z" class="iconDefaultFill"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/RepeatIcon.vue
Normal file
14
frontend/src/assets/Icons/RepeatIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 20.5001C16.6944 20.5001 20.5 16.6945 20.5 12.0001C20.5 9.17456 19.1213 6.67103 17 5.1255M13 22.4001L11 20.4001L13 18.4001M12 3.5001C7.30558 3.5001 3.5 7.30568 3.5 12.0001C3.5 14.8256 4.87867 17.3292 7 18.8747M11 5.6001L13 3.6001L11 1.6001" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/SaveIcon.vue
Normal file
14
frontend/src/assets/Icons/SaveIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15 8H8.6C8.03995 8 7.75992 8 7.54601 7.89101C7.35785 7.79513 7.20487 7.64215 7.10899 7.45399C7 7.24008 7 6.96005 7 6.4V3M17 21V14.6C17 14.0399 17 13.7599 16.891 13.546C16.7951 13.3578 16.6422 13.2049 16.454 13.109C16.2401 13 15.9601 13 15.4 13H8.6C8.03995 13 7.75992 13 7.54601 13.109C7.35785 13.2049 7.20487 13.3578 7.10899 13.546C7 13.7599 7 14.0399 7 14.6V21M21 9.32548V16.2C21 17.8802 21 18.7202 20.673 19.362C20.3854 19.9265 19.9265 20.3854 19.362 20.673C18.7202 21 17.8802 21 16.2 21H7.8C6.11984 21 5.27976 21 4.63803 20.673C4.07354 20.3854 3.6146 19.9265 3.32698 19.362C3 18.7202 3 17.8802 3 16.2V7.8C3 6.11984 3 5.27976 3.32698 4.63803C3.6146 4.07354 4.07354 3.6146 4.63803 3.32698C5.27976 3 6.11984 3 7.8 3H14.6745C15.1637 3 15.4083 3 15.6385 3.05526C15.8425 3.10425 16.0376 3.18506 16.2166 3.29472C16.4184 3.4184 16.5914 3.59135 16.9373 3.93726L20.0627 7.06274C20.4086 7.40865 20.5816 7.5816 20.7053 7.78343C20.8149 7.96237 20.8957 8.15746 20.9447 8.36154C21 8.59171 21 8.8363 21 9.32548Z" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
13
frontend/src/assets/Icons/SearchIcon.vue
Normal file
13
frontend/src/assets/Icons/SearchIcon.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0392 15.6244C18.2714 14.084 19.0082 12.1301 19.0082 10.0041C19.0082 5.03127 14.9769 1 10.0041 1C5.03127 1 1 5.03127 1 10.0041C1 14.9769 5.03127 19.0082 10.0041 19.0082C12.1301 19.0082 14.084 18.2714 15.6244 17.0392L21.2921 22.707C21.6828 23.0977 22.3163 23.0977 22.707 22.707C23.0977 22.3163 23.0977 21.6828 22.707 21.2921L17.0392 15.6244ZM10.0041 17.0173C6.1308 17.0173 2.99087 13.8774 2.99087 10.0041C2.99087 6.1308 6.1308 2.99087 10.0041 2.99087C13.8774 2.99087 17.0173 6.1308 17.0173 10.0041C17.0173 13.8774 13.8774 17.0173 10.0041 17.0173Z" class="iconDefaultFill"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/SetingsIcon.vue
Normal file
14
frontend/src/assets/Icons/SetingsIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3.5 8.5H15.5M15.5 8.5C15.5 10.1569 16.8431 11.5 18.5 11.5C20.1569 11.5 21.5 10.1568 21.5 8.5C21.5 6.84315 20.1569 5.5 18.5 5.5C16.8431 5.5 15.5 6.84315 15.5 8.5ZM9.5 16.5H21.5M9.5 16.5C9.5 18.1569 8.15685 19.5 6.5 19.5C4.84315 19.5 3.5 18.1569 3.5 16.5C3.5 14.8431 4.84315 13.5 6.5 13.5C8.15685 13.5 9.5 14.8431 9.5 16.5Z" class="iconDefaultStroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
16
frontend/src/assets/Icons/ShuffleIcon.vue
Normal file
16
frontend/src/assets/Icons/ShuffleIcon.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.448 2.03365C17.8385 1.64318 18.4716 1.64305 18.8621 2.03343L21.4146 4.58486C22.1959 5.36587 22.1961 6.63242 21.4149 7.41358L18.8675 9.96097C18.477 10.3515 17.8438 10.3515 17.4533 9.96097C17.0628 9.57045 17.0628 8.93728 17.4533 8.54676L19 7.00003H14.2361C13.8573 7.00003 13.511 7.21403 13.3416 7.55282L11.8954 10.4452L10.7699 8.2242L11.5528 6.65839C12.061 5.64204 13.0998 5.00003 14.2361 5.00003H19L17.4479 3.44794C17.0574 3.05741 17.0575 2.42418 17.448 2.03365Z" class="iconDefaultFill"/>
|
||||||
|
<path d="M17.448 14.0337C17.8385 13.6432 18.4716 13.643 18.8621 14.0334L21.4146 16.5849C22.1959 17.3659 22.1961 18.6324 21.4149 19.4136L18.8675 21.961C18.477 22.3515 17.8438 22.3515 17.4533 21.961C17.0628 21.5704 17.0628 20.9373 17.4533 20.5468L19 19H14.2361C13.0998 19 12.061 18.358 11.5528 17.3417L6.65836 7.55282C6.48897 7.21403 6.1427 7.00003 5.76393 7.00003H3C2.44772 7.00003 2 6.55232 2 6.00003C2 5.44775 2.44772 5.00003 3 5.00003H5.76393C6.90025 5.00003 7.93904 5.64204 8.44721 6.65839L13.3416 16.4472C13.511 16.786 13.8573 17 14.2361 17H19L17.4479 15.4479C17.0574 15.0574 17.0575 14.4242 17.448 14.0337Z" class="iconDefaultFill"/>
|
||||||
|
<path d="M8.12308 13.5178L9.24864 15.7388L8.44721 17.3417C7.93904 18.358 6.90025 19 5.76393 19H3C2.44772 19 2 18.5523 2 18C2 17.4477 2.44772 17 3 17H5.76393C6.1427 17 6.48897 16.786 6.65836 16.4472L8.12308 13.5178Z" class="iconDefaultFill"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
16
frontend/src/assets/Icons/SoundIcon.vue
Normal file
16
frontend/src/assets/Icons/SoundIcon.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4 1.79997C11.5532 0.262345 14 1.07796 14 2.99998V21.1214C14 23.0539 11.5313 23.8627 10.3878 22.3049L6.49356 17H4C2.34315 17 1 15.6568 1 14V9.99997C1 8.34312 2.34315 6.99997 4 6.99997H6.5L10.4 1.79997ZM12 2.99997L8.1 8.19997C7.72229 8.70358 7.12951 8.99997 6.5 8.99997H4C3.44772 8.99997 3 9.44769 3 9.99997V14C3 14.5523 3.44772 15 4 15H6.49356C7.13031 15 7.72901 15.3032 8.10581 15.8165L12 21.1214V2.99997Z" class="iconDefaultFill"/>
|
||||||
|
<path d="M16.2137 4.17442C16.1094 3.56448 16.5773 2.99997 17.1961 2.99997C17.6635 2.99997 18.0648 3.32797 18.1464 3.78821C18.4242 5.35344 19 8.96462 19 12C19 15.0353 18.4242 18.6465 18.1464 20.2117C18.0648 20.672 17.6635 21 17.1961 21C16.5773 21 16.1094 20.4355 16.2137 19.8255C16.5074 18.1073 17 14.8073 17 12C17 9.19261 16.5074 5.89267 16.2137 4.17442Z" class="iconDefaultFill"/>
|
||||||
|
<path d="M21.41 4.99997C20.7346 4.99997 20.2402 5.69394 20.3966 6.35095C20.6758 7.5241 21 9.43787 21 12C21 14.5621 20.6758 16.4758 20.3966 17.649C20.2402 18.306 20.7346 19 21.41 19C21.7716 19 22.0974 18.7944 22.2101 18.4508C22.5034 17.5569 23 15.5233 23 12C23 8.47669 22.5034 6.44303 22.2101 5.5491C22.0974 5.20553 21.7716 4.99997 21.41 4.99997Z" class="iconDefaultFill"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/Top.vue
Normal file
14
frontend/src/assets/Icons/Top.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21 3H3M12 21V7M12 7L5 14M12 7L19 14" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
23
frontend/src/assets/Icons/Trash.vue
Normal file
23
frontend/src/assets/Icons/Trash.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {defineProps} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: "#fff"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9 3H15M3 6H21M19 6L18.2987 16.5193C18.1935 18.0975 18.1409 18.8867 17.8 19.485C17.4999 20.0118 17.0472 20.4353 16.5017 20.6997C15.882 21 15.0911 21 13.5093 21H10.4907C8.90891 21 8.11803 21 7.49834 20.6997C6.95276 20.4353 6.50009 20.0118 6.19998 19.485C5.85911 18.8867 5.8065 18.0975 5.70129 16.5193L5 6"
|
||||||
|
:stroke="props.color" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/UploadIcon.vue
Normal file
14
frontend/src/assets/Icons/UploadIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21 15V16.2C21 17.8802 21 18.7202 20.673 19.362C20.3854 19.9265 19.9265 20.3854 19.362 20.673C18.7202 21 17.8802 21 16.2 21H7.8C6.11984 21 5.27976 21 4.63803 20.673C4.07354 20.3854 3.6146 19.9265 3.32698 19.362C3 18.7202 3 17.8802 3 16.2V15M17 8L12 3M12 3L7 8M12 3V15" class="iconFixedStrokeWhite" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
15
frontend/src/assets/Icons/UserIcon.vue
Normal file
15
frontend/src/assets/Icons/UserIcon.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg class="icon" width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 5.50008C6.65059 5.50008 7.58333 4.47407 7.58333 3.20841C7.58333 1.94276 6.65059 0.916748 5.5 0.916748C4.34941 0.916748 3.41667 1.94276 3.41667 3.20841C3.41667 4.47407 4.34941 5.50008 5.5 5.50008ZM5.5 4.58341C4.80964 4.58341 4.25 3.96781 4.25 3.20841C4.25 2.44902 4.80964 1.83341 5.5 1.83341C6.19036 1.83341 6.75 2.44902 6.75 3.20841C6.75 3.96781 6.19036 4.58341 5.5 4.58341Z" class="iconFixedFillWhite"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.45953 6.3787C8.10851 6.0265 7.56541 5.95053 7.13172 6.20787C7.05237 6.25495 6.98255 6.29804 6.91817 6.33778C6.74736 6.44319 6.6148 6.525 6.4439 6.59522C6.22917 6.68345 5.95145 6.75009 5.5 6.75009C5.05284 6.75009 4.77052 6.6872 4.55254 6.60205C4.35981 6.52676 4.20759 6.43217 4.00702 6.30752C3.9679 6.28321 3.92687 6.25771 3.88341 6.23103C3.44828 5.96392 2.88943 6.02599 2.52673 6.39254C2.36851 6.55243 2.18348 6.75921 2.03537 6.98451C1.89418 7.19929 1.75 7.48499 1.75 7.79175V8.83354C1.75 9.52398 2.30972 10.0834 3 10.0834H8C8.69028 10.0834 9.25 9.52398 9.25 8.83354V7.79175C9.25 7.48095 9.10197 7.19141 8.95815 6.9747C8.80707 6.74706 8.61883 6.53855 8.45953 6.3787ZM7.55696 6.92453C7.65662 6.8654 7.78251 6.8799 7.86928 6.96696C8.00893 7.10709 8.15517 7.2718 8.26381 7.43551C8.37971 7.61015 8.41667 7.73008 8.41667 7.79175V8.83354C8.41667 9.06358 8.2302 9.25009 8 9.25009H3C2.7698 9.25009 2.58333 9.06358 2.58333 8.83354V7.79175C2.58333 7.7313 2.61895 7.61381 2.73172 7.44227C2.83757 7.28125 2.98066 7.11857 3.11908 6.97869C3.20588 6.89096 3.33794 6.87401 3.44745 6.94123C3.48192 6.96239 3.51703 6.98431 3.55289 7.0067C3.75391 7.13219 3.97883 7.27259 4.24932 7.37826C4.58043 7.50761 4.96841 7.58342 5.5 7.58342C6.04175 7.58342 6.42971 7.50198 6.7606 7.36603C7.00025 7.26757 7.21164 7.13683 7.39827 7.02141C7.45329 6.98738 7.50616 6.95468 7.55696 6.92453Z" class="iconFixedFillWhite"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
frontend/src/assets/Icons/YoutubePlaylist.vue
Normal file
14
frontend/src/assets/Icons/YoutubePlaylist.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="1" y="1" width="68" height="68" rx="14" class="iconDefaultStroke" stroke-width="2"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.0018 24.4053C21.8356 24.5127 20.0955 26.2088 19.9549 28.3731C19.8202 30.4466 19.6875 32.9981 19.6875 35C19.6875 37.0019 19.8202 39.5534 19.9549 41.6269C20.0955 43.7912 21.8356 45.4874 24.0018 45.5947C27.1385 45.7501 31.6219 45.9375 35 45.9375C38.3781 45.9375 42.8615 45.7501 45.9982 45.5947C48.1644 45.4874 49.9045 43.7912 50.0451 41.6269C50.1798 39.5534 50.3125 37.0019 50.3125 35C50.3125 32.9981 50.1798 30.4466 50.0451 28.3731C49.9045 26.2088 48.1644 24.5127 45.9982 24.4053C42.8615 24.2499 38.3781 24.0625 35 24.0625C31.6219 24.0625 27.1385 24.2499 24.0018 24.4053ZM39.4375 35.4375L30.6875 39.8125V31.0625L39.4375 35.4375Z" class="iconDefaultFill"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
65
frontend/src/components/Header.vue
Normal file
65
frontend/src/components/Header.vue
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import InputSearch from "../items/InputSearch.vue";
|
||||||
|
import PlaylistIcon from "../assets/Icons/PlaylistIcon.vue";
|
||||||
|
import LectureListIcon from "../assets/Icons/LectureListIcon.vue";
|
||||||
|
import {lectureListIsOpen, lectureListPopUp, playlistPopUp, playlistsIsOpen} from "../stores/globalStore.ts";
|
||||||
|
import UploadIcon from "../assets/Icons/UploadIcon.vue";
|
||||||
|
|
||||||
|
const lectureList = lectureListIsOpen();
|
||||||
|
const playlists = playlistsIsOpen();
|
||||||
|
|
||||||
|
const toggleLectureList = () => {
|
||||||
|
playlists.closePlaylists();
|
||||||
|
lectureList.toggleLectureList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const togglePlaylists = () => {
|
||||||
|
lectureList.closeLectureList();
|
||||||
|
playlists.togglePlaylists();
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadMyFile = () => {
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.accept = ".MP4, .MP3, .WAV, .M4A, .AAC, .OGG, .FLAC";
|
||||||
|
input.click();
|
||||||
|
input.onchange = async (e) => {
|
||||||
|
const file = (e.target as HTMLInputElement).files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
console.log("File uploaded:", file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header>
|
||||||
|
<InputSearch />
|
||||||
|
<button @click="uploadMyFile">
|
||||||
|
<UploadIcon />
|
||||||
|
</button>
|
||||||
|
<button @click="togglePlaylists" v-if="playlistPopUp().popUp">
|
||||||
|
<PlaylistIcon />
|
||||||
|
</button>
|
||||||
|
<button @click="toggleLectureList" v-if="lectureListPopUp().popUp">
|
||||||
|
<LectureListIcon />
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 10px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
align-self: stretch;
|
||||||
|
background-color: var(--primary-500);
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
84
frontend/src/components/InfoHeader.vue
Normal file
84
frontend/src/components/InfoHeader.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import LogoIcon from "../assets/Icons/LogoIcon.vue";
|
||||||
|
import UserLoginInfo from "../items/UserLoginInfo.vue";
|
||||||
|
import SetingsIcon from "../assets/Icons/SetingsIcon.vue";
|
||||||
|
import DarkModeIcone from "../assets/Icons/DarkModeIcone.vue";
|
||||||
|
import LightModeIcone from "../assets/Icons/LightModeIcone.vue";
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import router from "../router.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const themeIs = ref("dark");
|
||||||
|
|
||||||
|
function switchThemeMode() {
|
||||||
|
const currentTheme = themeIs.value;
|
||||||
|
const newTheme = currentTheme === "dark" ? "light" : "dark";
|
||||||
|
localStorage.setItem("theme", newTheme);
|
||||||
|
document.documentElement.setAttribute("data-theme", newTheme);
|
||||||
|
themeIs.value = newTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const storedTheme = localStorage.getItem("theme");
|
||||||
|
themeIs.value = storedTheme || "dark";
|
||||||
|
document.documentElement.setAttribute("data-theme", storedTheme || "dark");
|
||||||
|
});
|
||||||
|
|
||||||
|
function navigateToSettings() {
|
||||||
|
router.push("/settings");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="info-header">
|
||||||
|
<div class="info-header__content">
|
||||||
|
<div class="info-header__content__logo">
|
||||||
|
<LogoIcon />
|
||||||
|
<h1>Subsonics</h1>
|
||||||
|
</div>
|
||||||
|
<UserLoginInfo />
|
||||||
|
<button
|
||||||
|
class="dark-mode-toggle"
|
||||||
|
@click="switchThemeMode"
|
||||||
|
:class="themeIs == 'dark' ? 'dark-mode' : 'light-mode'"
|
||||||
|
:aria-label="themeIs == 'dark' ? 'Switch to light mode' : 'Switch to dark mode'"
|
||||||
|
>
|
||||||
|
<DarkModeIcone v-if="themeIs == 'dark'" />
|
||||||
|
<LightModeIcone v-if="themeIs == 'light'" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="navigateToSettings"
|
||||||
|
>
|
||||||
|
<SetingsIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.info-header {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 25px;
|
||||||
|
background: var(--neutral-100);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 0px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
&__logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
127
frontend/src/components/LectureList.vue
Normal file
127
frontend/src/components/LectureList.vue
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import MiniatureList from "../items/MiniatureList.vue";
|
||||||
|
import {defineProps, ref} from "vue";
|
||||||
|
import {historyListStore, lectureListStore} from "../stores/dataStore.ts";
|
||||||
|
import InPlayListIcon from "../assets/Icons/InPlayListIcon.vue";
|
||||||
|
import Trash from "../assets/Icons/Trash.vue";
|
||||||
|
import SwitchTab from "../items/SwitchTab.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
popup: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const nav = ref("lecture");
|
||||||
|
|
||||||
|
const switchNav = (tabName: string) => {
|
||||||
|
nav.value = tabName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lecturesList = lectureListStore();
|
||||||
|
const historyList = historyListStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="props.popup == true ? 'lecture-list--popup' : 'lecture-list'">
|
||||||
|
<div class="lecture-list__content">
|
||||||
|
<div class="lecture-list__header">
|
||||||
|
<SwitchTab
|
||||||
|
:selectedTab="nav == 'lecture' ? 1 : 2"
|
||||||
|
tab1-label="Liste de lecture"
|
||||||
|
:tab1-click="() => {switchNav('lecture')}"
|
||||||
|
tab2-label="Historique de lecture"
|
||||||
|
:tab2-click="() => {switchNav('history')}"
|
||||||
|
/>
|
||||||
|
<div class="sub_header" v-if="nav == 'lecture'">
|
||||||
|
<div>
|
||||||
|
<p>{{ lecturesList.lectures.length }}</p>
|
||||||
|
<InPlayListIcon/>
|
||||||
|
</div>
|
||||||
|
<button>
|
||||||
|
<Trash color="#FF306F"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<MiniatureList
|
||||||
|
v-if="nav == 'lecture'"
|
||||||
|
v-for="lecture in lecturesList.lectures"
|
||||||
|
:thumbnail="lecture.thumbnail"
|
||||||
|
:title="lecture.title"
|
||||||
|
:name="lecture.name"
|
||||||
|
/>
|
||||||
|
<MiniatureList
|
||||||
|
v-if="nav == 'history'"
|
||||||
|
v-for="history in historyList.history"
|
||||||
|
:thumbnail="history.thumbnail"
|
||||||
|
:title="history.title"
|
||||||
|
:name="history.name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.lecture-list--popup {
|
||||||
|
position: absolute;
|
||||||
|
height: 90vh;
|
||||||
|
width: 90vw;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lecture-list {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lecture-list--popup, .lecture-list {
|
||||||
|
display: flex;
|
||||||
|
border-radius: 25px;
|
||||||
|
background: var(--neutral-100);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
||||||
|
.sub_header {
|
||||||
|
flex : 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
180
frontend/src/components/Player.vue
Normal file
180
frontend/src/components/Player.vue
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import RepeatIcon from "../assets/Icons/RepeatIcon.vue";
|
||||||
|
import ShuffleIcon from "../assets/Icons/ShuffleIcon.vue";
|
||||||
|
import HandIcon from "../assets/Icons/HandIcon.vue";
|
||||||
|
import LeaveIcon from "../assets/Icons/LeaveIcon.vue";
|
||||||
|
import PrevIcon from "../assets/Icons/PrevIcon.vue";
|
||||||
|
import NextIcon from "../assets/Icons/NextIcon.vue";
|
||||||
|
import PlayIcon from "../assets/Icons/PlayIcon.vue";
|
||||||
|
import PauseIcon from "../assets/Icons/PauseIcon.vue";
|
||||||
|
import {ref} from "vue";
|
||||||
|
|
||||||
|
const isPlaying = ref(true);
|
||||||
|
|
||||||
|
function togglePlay() {
|
||||||
|
isPlaying.value = !isPlaying.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const progress = ref(0);
|
||||||
|
|
||||||
|
function handleTimebarClick(event: MouseEvent) {
|
||||||
|
const target = event.currentTarget as HTMLElement;
|
||||||
|
const rect = target.getBoundingClientRect();
|
||||||
|
const clickX = event.clientX - rect.left;
|
||||||
|
const width = rect.width;
|
||||||
|
progress.value = Math.min(Math.max((clickX / width) * 100, 0), 100);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="player">
|
||||||
|
<div class="player__video_content">
|
||||||
|
<div class="player__video_content__img">
|
||||||
|
<img src="https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg" alt="Video Thumbnail" />
|
||||||
|
</div>
|
||||||
|
<p>lofi hip hop mix 📚 beats to relax/study to (Part 1)</p>
|
||||||
|
</div>
|
||||||
|
<div class="player__timebar" @click="handleTimebarClick">
|
||||||
|
<div
|
||||||
|
class="player__timebar__progress"
|
||||||
|
:style="{ width: progress + '%' }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div class="player__timebar__time">
|
||||||
|
<p>2:10</p>
|
||||||
|
<p>4:20</p>
|
||||||
|
</div>
|
||||||
|
<div class="player__controls">
|
||||||
|
<button><PrevIcon /></button>
|
||||||
|
<button
|
||||||
|
class="play_button"
|
||||||
|
@click="togglePlay"
|
||||||
|
>
|
||||||
|
<PauseIcon v-if="isPlaying"/>
|
||||||
|
<PlayIcon v-else />
|
||||||
|
</button>
|
||||||
|
<button><NextIcon /></button>
|
||||||
|
</div>
|
||||||
|
<div class="player__actions">
|
||||||
|
<button
|
||||||
|
title="Jouer le song en boucle"
|
||||||
|
>
|
||||||
|
<RepeatIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Joyer les songs aléatoirement"
|
||||||
|
>
|
||||||
|
<ShuffleIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Rammener le bot dans le channel"
|
||||||
|
>
|
||||||
|
<HandIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Faire partir le bot"
|
||||||
|
>
|
||||||
|
<LeaveIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.player {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 30px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 25px;
|
||||||
|
background: var(--neutral-100);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&__video_content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
align-self: stretch;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
flex: 1 0 0;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 10px;
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__timebar {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
background: var(--neutral-300);
|
||||||
|
border-radius: 900px;
|
||||||
|
position: relative;
|
||||||
|
transition: height 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__progress {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--primary-500);
|
||||||
|
border-radius: 900px;
|
||||||
|
transition: width 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__time {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--neutral-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.play_button {
|
||||||
|
background: var(--primary-500);
|
||||||
|
display: flex;
|
||||||
|
padding: 16px 15px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
border-radius: 900px;
|
||||||
|
}
|
||||||
|
</style>
|
73
frontend/src/components/Playlist.vue
Normal file
73
frontend/src/components/Playlist.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps } from 'vue';
|
||||||
|
import PlaylistItem from "../items/PlaylistItem.vue";
|
||||||
|
import {playlistsListStore} from "../stores/dataStore.ts";
|
||||||
|
import Button from "../items/Button.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
popup: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const playlists = playlistsListStore();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div :class="props.popup == true ? 'playlist--popup' : 'playlist'">
|
||||||
|
<div class="playlist__content">
|
||||||
|
<PlaylistItem
|
||||||
|
v-for="playlist in playlists.playlists"
|
||||||
|
:key="playlist.name"
|
||||||
|
:title="playlist.name"
|
||||||
|
:imgSrc="playlist.origin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:on-click="() => {}"
|
||||||
|
:disabled="false"
|
||||||
|
>
|
||||||
|
Créer une playlist
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.playlist--popup {
|
||||||
|
position: absolute;
|
||||||
|
height: 90vh;
|
||||||
|
width: 90vw;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist--popup, .playlist {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 25px;
|
||||||
|
background: var(--neutral-100);
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
51
frontend/src/components/SearchPreview.vue
Normal file
51
frontend/src/components/SearchPreview.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import Miniature from "../items/Miniature.vue";
|
||||||
|
import {searchStore} from "../stores/dataStore.ts";
|
||||||
|
import Loader from "../items/Loader.vue";
|
||||||
|
|
||||||
|
const search = searchStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="search-preview">
|
||||||
|
<div class="search-preview__content">
|
||||||
|
<Miniature
|
||||||
|
v-if="search.videos != undefined"
|
||||||
|
v-for="video in search.videos"
|
||||||
|
:key="video.title"
|
||||||
|
:thumbnail="video.thumbnail"
|
||||||
|
:title="video.title"
|
||||||
|
:name="video.name"
|
||||||
|
:duration="video.duration"
|
||||||
|
/>
|
||||||
|
<Loader
|
||||||
|
v-else
|
||||||
|
class="search-preview__loader"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.search-preview {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 25px;
|
||||||
|
background: var(--neutral-100);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(176px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
38
frontend/src/components/SettingsHeader.vue
Normal file
38
frontend/src/components/SettingsHeader.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import LogoIcon from "../assets/Icons/LogoIcon.vue";
|
||||||
|
import router from "../router.ts";
|
||||||
|
|
||||||
|
function navigateToHome() {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header>
|
||||||
|
<button
|
||||||
|
@click="navigateToHome"
|
||||||
|
class="logo">
|
||||||
|
<LogoIcon/>
|
||||||
|
<h1>Subsonics</h1>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 25px;
|
||||||
|
background-color: var(--neutral-100);
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
67
frontend/src/items/Button.vue
Normal file
67
frontend/src/items/Button.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {defineProps} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
type: 'primary' | 'secondary' | 'tertiary';
|
||||||
|
disabled: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
:class="['button', `button--${props.type}`]"
|
||||||
|
:disabled="props.disabled"
|
||||||
|
@click="props.onClick"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
background-color: var(--primary-500);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--primary-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--secondary {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--neutral-50);
|
||||||
|
border: 1px solid var(--neutral-950);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--neutral-950);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--tertiary {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--neutral-700);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--neutral-100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
80
frontend/src/items/InputSearch.vue
Normal file
80
frontend/src/items/InputSearch.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {lectureListIsOpen, playlistsIsOpen} from '../stores/globalStore.ts'
|
||||||
|
import {searchStore} from '../stores/dataStore.ts'
|
||||||
|
import SearchIcon from "../assets/Icons/SearchIcon.vue";
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
|
||||||
|
const search = searchStore();
|
||||||
|
|
||||||
|
const lectureList = lectureListIsOpen();
|
||||||
|
const playlists = playlistsIsOpen();
|
||||||
|
|
||||||
|
const inputRef = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const updateSearch = (event: Event) => {
|
||||||
|
lectureList.closeLectureList();
|
||||||
|
playlists.closePlaylists();
|
||||||
|
search.updateSearch((event.target as HTMLInputElement).value);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (inputRef.value) {
|
||||||
|
inputRef.value.value = search.searchQuery;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="group">
|
||||||
|
<SearchIcon />
|
||||||
|
<input
|
||||||
|
placeholder="Search"
|
||||||
|
type="search"
|
||||||
|
class="input"
|
||||||
|
@input="updateSearch"
|
||||||
|
ref="inputRef"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
line-height: 28px;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 28px;
|
||||||
|
padding: 0 1rem;
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 9px;
|
||||||
|
outline: none;
|
||||||
|
background-color: var(--neutral-50);
|
||||||
|
color: var(--neutral-950);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input::placeholder {
|
||||||
|
color: var(--neutral-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:focus, input:hover {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-800);
|
||||||
|
background-color: var(--neutral-100);
|
||||||
|
box-shadow: 0 0 0 4px rgb(234 76 137 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 1rem;
|
||||||
|
fill: #9e9ea7;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
163
frontend/src/items/Loader.vue
Normal file
163
frontend/src/items/Loader.vue
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="loader">
|
||||||
|
<svg viewBox="0 0 80 80">
|
||||||
|
<circle r="32" cy="40" cx="40" id="test"></circle>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* From Uiverse.io by mobinkakei */
|
||||||
|
.loader {
|
||||||
|
--path: var(--neutral-950);
|
||||||
|
--dot: var(--primary-500);
|
||||||
|
--duration: 2.5s;
|
||||||
|
--size: 44px;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:before {
|
||||||
|
content: "";
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
background: var(--dot);
|
||||||
|
top: 37px;
|
||||||
|
left: 19px;
|
||||||
|
transform: translate(-18px, -18px);
|
||||||
|
animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86)
|
||||||
|
infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader svg {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader svg rect,
|
||||||
|
.loader svg polygon,
|
||||||
|
.loader svg circle {
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--path);
|
||||||
|
stroke-width: 10px;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader svg polygon {
|
||||||
|
stroke-dasharray: 145 76 145 76;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86)
|
||||||
|
infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader svg rect {
|
||||||
|
stroke-dasharray: 192 64 192 64;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader svg circle {
|
||||||
|
stroke-dasharray: 150 50 150 50;
|
||||||
|
stroke-dashoffset: 75;
|
||||||
|
animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86)
|
||||||
|
infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pathTriangle {
|
||||||
|
33% {
|
||||||
|
stroke-dashoffset: 74;
|
||||||
|
}
|
||||||
|
|
||||||
|
66% {
|
||||||
|
stroke-dashoffset: 147;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 221;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dotTriangle {
|
||||||
|
33% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
66% {
|
||||||
|
transform: translate(10px, -18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate(-10px, -18px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pathRect {
|
||||||
|
25% {
|
||||||
|
stroke-dashoffset: 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
stroke-dashoffset: 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
stroke-dashoffset: 192;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 256;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dotRect {
|
||||||
|
25% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translate(18px, -18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: translate(0, -36px);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate(-18px, -18px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pathCircle {
|
||||||
|
25% {
|
||||||
|
stroke-dashoffset: 125;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
stroke-dashoffset: 175;
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
stroke-dashoffset: 225;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 275;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
136
frontend/src/items/Miniature.vue
Normal file
136
frontend/src/items/Miniature.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps } from 'vue';
|
||||||
|
import Add from "../assets/Icons/Add.vue";
|
||||||
|
import Play from "../assets/Icons/Play.vue";
|
||||||
|
import SaveIcon from "../assets/Icons/SaveIcon.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
thumbnail: string;
|
||||||
|
title: string;
|
||||||
|
name: string;
|
||||||
|
duration: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="miniature">
|
||||||
|
<div class="miniature__img">
|
||||||
|
<img :src="props.thumbnail" alt="Video Thumbnail" />
|
||||||
|
<p class="miniature__img__duration" >{{duration}}</p>
|
||||||
|
<div class="miniature__img__overlay">
|
||||||
|
<button
|
||||||
|
title="Ajouter à la liste de lecture"
|
||||||
|
>
|
||||||
|
<Add />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Jouer le song maintenant"
|
||||||
|
>
|
||||||
|
<Play />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Ajouter à la playlist"
|
||||||
|
>
|
||||||
|
<SaveIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="miniature__info">
|
||||||
|
<p class="miniature__info__title" :title="props.title">{{ props.title }}</p>
|
||||||
|
<p class="miniature__info__name" :title="props.name">{{ props.name }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.miniature {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 0 0;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__duration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
color: white;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .miniature__img__overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: var(--neutral-500);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
158
frontend/src/items/MiniatureList.vue
Normal file
158
frontend/src/items/MiniatureList.vue
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {defineProps, onBeforeUnmount, onMounted, ref} from 'vue';
|
||||||
|
import DotsMenu from "../assets/Icons/DotsMenu.vue";
|
||||||
|
import Trash from "../assets/Icons/Trash.vue";
|
||||||
|
import Play from "../assets/Icons/Play.vue";
|
||||||
|
import Add from "../assets/Icons/Add.vue";
|
||||||
|
import Top from "../assets/Icons/Top.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
thumbnail: string;
|
||||||
|
title: string;
|
||||||
|
name: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let dotsMenuActive = ref(false);
|
||||||
|
|
||||||
|
// Fonction pour afficher / masquer le menu
|
||||||
|
const toggleMenu = (event: MouseEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
dotsMenuActive.value = !dotsMenuActive.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fermer le menu si on clique en dehors
|
||||||
|
const closeMenu = () => {
|
||||||
|
dotsMenuActive.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ajoute l'écouteur pour fermer au clic global
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener("click", closeMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Supprime l'écouteur quand le composant est démonté
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener("click", closeMenu);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="miniature-list">
|
||||||
|
<div class="miniature-list__img">
|
||||||
|
<img :src="props.thumbnail" alt="Video Thumbnail"/>
|
||||||
|
</div>
|
||||||
|
<div class="miniature-list__info">
|
||||||
|
<p class="miniature-list__info__title" :title="props.title">{{ props.title }}</p>
|
||||||
|
<p class="miniature-list__info__name" :title="props.name">{{ props.name }}</p>
|
||||||
|
</div>
|
||||||
|
<button @click.stop="toggleMenu">
|
||||||
|
<DotsMenu/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div v-if="dotsMenuActive" class="overlay" @click="closeMenu"></div>
|
||||||
|
<div :class="['miniature-list__dots-menu', { 'miniature-list__dots-menu--active': dotsMenuActive }]">
|
||||||
|
<button>
|
||||||
|
<Top/>
|
||||||
|
Placer au dessus
|
||||||
|
</button>
|
||||||
|
<button>
|
||||||
|
<Play/>
|
||||||
|
Lire
|
||||||
|
</button>
|
||||||
|
<button>
|
||||||
|
<Add/>
|
||||||
|
Ajouter à une playlist
|
||||||
|
</button>
|
||||||
|
<button>
|
||||||
|
<Trash/>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.miniature-list {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
width: 150px;
|
||||||
|
height: 100px;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
&__title, &__name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: transparent;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dots-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
gap: 5px;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 10;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
155
frontend/src/items/PlaylistItem.vue
Normal file
155
frontend/src/items/PlaylistItem.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {defineProps, ref, onMounted, onBeforeUnmount} from 'vue';
|
||||||
|
import DotsMenu from "../assets/Icons/DotsMenu.vue";
|
||||||
|
import YoutubePlaylist from "../assets/Icons/YoutubePlaylist.vue";
|
||||||
|
import Trash from "../assets/Icons/Trash.vue";
|
||||||
|
import Add from "../assets/Icons/Add.vue";
|
||||||
|
import Play from "../assets/Icons/Play.vue";
|
||||||
|
import DefaultPlaylist from "../assets/Icons/DefaultPlaylist.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
imgSrc: string;
|
||||||
|
title: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let dotsMenuActive = ref(false);
|
||||||
|
|
||||||
|
const toggleMenu = (event: MouseEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
dotsMenuActive.value = !dotsMenuActive.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeMenu = () => {
|
||||||
|
dotsMenuActive.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener("click", closeMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener("click", closeMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
function oppenPlaylist() {
|
||||||
|
//todo: ouvrir la playlist
|
||||||
|
console.log("Ouvrir la playlist");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="play-list">
|
||||||
|
<button
|
||||||
|
class="play-list__content"
|
||||||
|
@click.stop="oppenPlaylist"
|
||||||
|
>
|
||||||
|
<YoutubePlaylist v-if="imgSrc == 'youtube'"/>
|
||||||
|
<DefaultPlaylist v-else/>
|
||||||
|
<div class="play-list__info">
|
||||||
|
<p class="play-list__info__title" :title="props.title">{{ props.title }}</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button @click.stop="toggleMenu">
|
||||||
|
<DotsMenu/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div v-if="dotsMenuActive" class="overlay" @click="closeMenu"></div>
|
||||||
|
<div :class="['play-list__dots-menu', { 'play-list__dots-menu--active': dotsMenuActive }]">
|
||||||
|
<button>
|
||||||
|
<Add/>
|
||||||
|
Ajouter
|
||||||
|
</button>
|
||||||
|
<button>
|
||||||
|
<Play/>
|
||||||
|
Lire
|
||||||
|
</button>
|
||||||
|
<button>
|
||||||
|
<Trash/>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.play-list {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
&__info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
text-align: start;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: transparent;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dots-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
gap: 5px;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 10;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
78
frontend/src/items/SwitchTab.vue
Normal file
78
frontend/src/items/SwitchTab.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {defineProps} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
selectedTab?: 1 | 2;
|
||||||
|
tab1Click?: (event: MouseEvent) => void;
|
||||||
|
tab1Label?: string;
|
||||||
|
tab2Click?: (event: MouseEvent) => void;
|
||||||
|
tab2Label?: string;
|
||||||
|
tab1Lecture?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="tab-container">
|
||||||
|
<button v-on:click="props.tab1Click" class="tab tab--1">{{ props.tab1Label }}</button>
|
||||||
|
<button v-on:click="props.tab2Click" class="tab tab--2">{{ props.tab2Label }}</button>
|
||||||
|
<div class="indicator" :class="props.selectedTab == 1 ? 'indicator-left' : 'indicator-right'" ></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tab-container {
|
||||||
|
width: 79%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
position: relative;
|
||||||
|
padding: 2px;
|
||||||
|
background-color: var(--neutral-50);
|
||||||
|
border-radius: 9px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator {
|
||||||
|
content: "";
|
||||||
|
width: 50%;
|
||||||
|
height: 85%;
|
||||||
|
background: var(--neutral-100);
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
z-index: 9;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.04);
|
||||||
|
border-radius: 7px;
|
||||||
|
transition: left 0.2s ease-out;
|
||||||
|
}
|
||||||
|
.indicator-left {
|
||||||
|
left: 2px;
|
||||||
|
}
|
||||||
|
.indicator-right {
|
||||||
|
left: calc(50% - 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
width: 50%;
|
||||||
|
height: 28px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
flex: none;
|
||||||
|
align-self: stretch;
|
||||||
|
flex-grow: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab--1:hover ~ .indicator {
|
||||||
|
left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab--2:hover ~ .indicator {
|
||||||
|
left: calc(50% - 2px);
|
||||||
|
}
|
||||||
|
</style>
|
26
frontend/src/items/UserLoginInfo.vue
Normal file
26
frontend/src/items/UserLoginInfo.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import UserIcon from "../assets/Icons/UserIcon.vue";
|
||||||
|
import {userOnlineStore} from "../stores/dataStore.ts";
|
||||||
|
|
||||||
|
const userLoginInfo = userOnlineStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="user-login-info">
|
||||||
|
<UserIcon />
|
||||||
|
<p class="sub-text colorFixedWhite">{{ userLoginInfo.nbUser }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.user-login-info {
|
||||||
|
display: flex;
|
||||||
|
padding: 3px 10px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--primary-500);
|
||||||
|
color: var(--neutral-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
13
frontend/src/main.ts
Normal file
13
frontend/src/main.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
import './assets/Global.scss'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
const pinia = createPinia();
|
||||||
|
import router from './router';
|
||||||
|
|
||||||
|
|
||||||
|
app.use(pinia);
|
||||||
|
app.use(router);
|
||||||
|
app.mount('#app');
|
18
frontend/src/router.ts
Normal file
18
frontend/src/router.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import Home from './views/Home.vue';
|
||||||
|
import Login from './views/DiscordLogin.vue';
|
||||||
|
import Settings from './views/Settings.vue';
|
||||||
|
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{ path: '/', component: Home },
|
||||||
|
{ path: '/login', component: Login },
|
||||||
|
{ path: '/settings', component: Settings },
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
197
frontend/src/stores/dataStore.ts
Normal file
197
frontend/src/stores/dataStore.ts
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import {defineStore} from 'pinia';
|
||||||
|
|
||||||
|
export const userOnlineStore = defineStore('userOnline', {
|
||||||
|
state: () => ({
|
||||||
|
nbUser: 1
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
updateUserOnline(nbUser: number) {
|
||||||
|
this.nbUser = nbUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const searchStore = defineStore('search', {
|
||||||
|
state: () => ({
|
||||||
|
searchQuery: '',
|
||||||
|
videos: [
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 1)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
duration: '2:00:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 2)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
duration: '1:00:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 3)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
duration: '3:00:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 4)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
duration: '00:30:00',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateSearch(query: string) {
|
||||||
|
this.searchQuery = query;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateVideosSearch(videoList: { name: string; title: string; thumbnail: string; duration: string }[]) {
|
||||||
|
this.videos = videoList;
|
||||||
|
},
|
||||||
|
|
||||||
|
clearVideosSearch() {
|
||||||
|
this.videos = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const playlistsListStore = defineStore('playlist', {
|
||||||
|
state: () => ({
|
||||||
|
playlists: [
|
||||||
|
{
|
||||||
|
name: 'Le japon ça pleure beaucoup',
|
||||||
|
origin: 'YouTube',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Le japon ça bouge vraiment beaucoup',
|
||||||
|
origin: 'YouTube',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cofee time',
|
||||||
|
origin: 'YouTube',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pop 20',
|
||||||
|
origin: 'YouTube',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updatePlaylistsList(playlist: { name: string; origin: 'YouTube' }[]) {
|
||||||
|
this.playlists = playlist;
|
||||||
|
},
|
||||||
|
|
||||||
|
clearPlaylistsList() {
|
||||||
|
this.playlists = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const playlist = defineStore('playlist', {
|
||||||
|
state: () => ({
|
||||||
|
playlist: [
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 1)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 2)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 3)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 4)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updatePlaylist(videoList: { name: string; title: string; thumbnail: string }[]) {
|
||||||
|
this.playlist = videoList;
|
||||||
|
},
|
||||||
|
clearPlaylist() {
|
||||||
|
this.playlist = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const lectureListStore = defineStore('lecture', {
|
||||||
|
state: () => ({
|
||||||
|
lectures: [
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 1)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 2)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 3)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 4)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateLectureList(lecture: { name: string; title: string; thumbnail: string }[]) {
|
||||||
|
this.lectures = lecture;
|
||||||
|
},
|
||||||
|
|
||||||
|
clearLectureList() {
|
||||||
|
this.lectures = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const historyListStore = defineStore('history', {
|
||||||
|
state: () => ({
|
||||||
|
history: [
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 2)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 3)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LofiGirl',
|
||||||
|
title: 'lofi hip hop mix 📚 beats to relax/study to (Part 4)',
|
||||||
|
thumbnail: 'https://i.ytimg.com/vi/CFGLoQIhmow/hqdefault.jpg',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateHistoryList(lecture: { name: string; title: string; thumbnail: string }[]) {
|
||||||
|
this.history = lecture;
|
||||||
|
},
|
||||||
|
|
||||||
|
clearHistoryList() {
|
||||||
|
this.history = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
131
frontend/src/stores/dataStoreFuture.ts
Normal file
131
frontend/src/stores/dataStoreFuture.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const userOnlineStore = defineStore('userOnline', {
|
||||||
|
state: () => ({
|
||||||
|
nbUser: null as number | null
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
updateUserOnline(nbUser: number) {
|
||||||
|
this.nbUser = nbUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const searchStore = defineStore('search', {
|
||||||
|
state: () => ({
|
||||||
|
searchQuery: '',
|
||||||
|
videos: null as {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
duration: string;
|
||||||
|
}[] | null
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateSearch(query: string) {
|
||||||
|
this.searchQuery = query;
|
||||||
|
},
|
||||||
|
updateVideosSearch(videoList: {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
duration: string;
|
||||||
|
}[]) {
|
||||||
|
this.videos = videoList;
|
||||||
|
},
|
||||||
|
clearVideosSearch() {
|
||||||
|
this.videos = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const playlistsListStore = defineStore('playlistsList', {
|
||||||
|
state: () => ({
|
||||||
|
playlists: null as {
|
||||||
|
name: string;
|
||||||
|
origin: 'Default' | 'YouTube' | 'Spotify' | 'SoundCloud';
|
||||||
|
}[] | null
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updatePlaylistsList(playlists: {
|
||||||
|
name: string;
|
||||||
|
origin: 'Default' | 'YouTube' | 'Spotify' | 'SoundCloud';
|
||||||
|
}[]) {
|
||||||
|
this.playlists = playlists;
|
||||||
|
},
|
||||||
|
clearPlaylistsList() {
|
||||||
|
this.playlists = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const playlistStore = defineStore('playlist', {
|
||||||
|
state: () => ({
|
||||||
|
playlist: null as {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
}[] | null
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updatePlaylist(videos: {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
}[]) {
|
||||||
|
this.playlist = videos;
|
||||||
|
},
|
||||||
|
clearPlaylist() {
|
||||||
|
this.playlist = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const lectureListStore = defineStore('lecture', {
|
||||||
|
state: () => ({
|
||||||
|
lectures: null as {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
}[] | null
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateLectureList(lectures: {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
}[]) {
|
||||||
|
this.lectures = lectures;
|
||||||
|
},
|
||||||
|
clearLectureList() {
|
||||||
|
this.lectures = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const historyListStore = defineStore('history', {
|
||||||
|
state: () => ({
|
||||||
|
history: null as {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
}[] | null
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateHistoryList(lectures: {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
}[]) {
|
||||||
|
this.history = lectures;
|
||||||
|
},
|
||||||
|
clearHistoryList() {
|
||||||
|
this.history = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
58
frontend/src/stores/globalStore.ts
Normal file
58
frontend/src/stores/globalStore.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const playlistsIsOpen = defineStore('playlistsIsOpen', {
|
||||||
|
state: () => ({
|
||||||
|
isOpen: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
togglePlaylists() {
|
||||||
|
this.isOpen = !this.isOpen;
|
||||||
|
},
|
||||||
|
closePlaylists() {
|
||||||
|
this.isOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const playlistPopUp = defineStore('PlaylistNoPopUp', {
|
||||||
|
state: () => ({
|
||||||
|
popUp: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
playlistNoPopUp() {
|
||||||
|
this.popUp = false;
|
||||||
|
},
|
||||||
|
playlistPopUp() {
|
||||||
|
this.popUp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const lectureListIsOpen = defineStore('lectureListIsOpen', {
|
||||||
|
state: () => ({
|
||||||
|
isOpen: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
toggleLectureList() {
|
||||||
|
this.isOpen = !this.isOpen;
|
||||||
|
},
|
||||||
|
closeLectureList() {
|
||||||
|
this.isOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const lectureListPopUp = defineStore('lectureListNoPopUp', {
|
||||||
|
state: () => ({
|
||||||
|
popUp: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
lectureListNoPopUp() {
|
||||||
|
this.popUp = false;
|
||||||
|
},
|
||||||
|
lectureListPopUp() {
|
||||||
|
this.popUp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
89
frontend/src/views/DiscordLogin.vue
Normal file
89
frontend/src/views/DiscordLogin.vue
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import LogoIcon from "../assets/Icons/LogoIcon.vue";
|
||||||
|
import Button from "../items/Button.vue";
|
||||||
|
import DiscordLogo from "../assets/Icons/DiscordLogo.vue";
|
||||||
|
import router from "../router.ts";
|
||||||
|
|
||||||
|
function login() {
|
||||||
|
console.log("login");
|
||||||
|
router.push({ path: '/' })
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
<div class="container">
|
||||||
|
<div class="logo">
|
||||||
|
<LogoIcon/>
|
||||||
|
<h1>Subsonics</h1>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:disabled="false"
|
||||||
|
:on-click="login"
|
||||||
|
>
|
||||||
|
<DiscordLogo/>
|
||||||
|
</Button>
|
||||||
|
<p>
|
||||||
|
Fait avec le 🤍 par Raphix
|
||||||
|
</p>
|
||||||
|
<p class="sub-text">Version : 3.0.0</p>
|
||||||
|
<p class="sub-text information">
|
||||||
|
*L'accès à l'interface nécessite une connexion via Discord, et implique d'être membre du serveur Discord du CLP. En poursuivant la connexion sur ce site, vous consentez à l'utilisation de vos données fournies par Discord dans le but de garantir le bon fonctionnement du site. Vous avez la possibilité de demander à tout moment la suppression de vos données à Raphix (raphixscrap). Tout acte intentionnel de dégradation du bot est passible d'un bannissement du site.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
background-color: var(--neutral-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: var(--neutral-100);
|
||||||
|
border-radius: 8px;
|
||||||
|
gap: 20px;
|
||||||
|
color: var(--neutral-950);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-text {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--neutral-950);
|
||||||
|
}
|
||||||
|
|
||||||
|
.information {
|
||||||
|
color: var(--neutral-400);
|
||||||
|
}
|
||||||
|
</style>
|
77
frontend/src/views/Home.vue
Normal file
77
frontend/src/views/Home.vue
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {ref, onMounted, onUnmounted, computed} from "vue";
|
||||||
|
import HomeMobile from "./HomeMobile.vue";
|
||||||
|
import HomeTablet from "./HomeTablet.vue";
|
||||||
|
import HomeDesktop from "./HomeDesktop.vue";
|
||||||
|
import {lectureListPopUp, playlistPopUp} from "../stores/globalStore.ts";
|
||||||
|
|
||||||
|
const screenSize = ref("mobile");
|
||||||
|
|
||||||
|
const playlist = playlistPopUp();
|
||||||
|
const lectureList = lectureListPopUp();
|
||||||
|
|
||||||
|
const updateScreenSize = () => {
|
||||||
|
const width = window.innerWidth;
|
||||||
|
|
||||||
|
if (width < 768) {
|
||||||
|
screenSize.value = "mobile";
|
||||||
|
playlist.playlistPopUp()
|
||||||
|
lectureList.lectureListPopUp()
|
||||||
|
} else if (width >= 768 && width < 1200) {
|
||||||
|
screenSize.value = "tablet";
|
||||||
|
playlist.playlistNoPopUp()
|
||||||
|
lectureList.lectureListPopUp()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
screenSize.value = "desktop";
|
||||||
|
playlist.playlistNoPopUp()
|
||||||
|
lectureList.lectureListNoPopUp()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateScreenSize();
|
||||||
|
window.addEventListener("resize", updateScreenSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("resize", updateScreenSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
const CurrentComponent = computed(() => {
|
||||||
|
switch (screenSize.value) {
|
||||||
|
case 'mobile':
|
||||||
|
return HomeMobile;
|
||||||
|
case 'tablet':
|
||||||
|
return HomeTablet;
|
||||||
|
case 'desktop':
|
||||||
|
return HomeDesktop;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="home-container">
|
||||||
|
<component v-if="CurrentComponent" :is="CurrentComponent" />
|
||||||
|
<p v-else>Composant introuvable</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
flex: 1 0 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
133
frontend/src/views/HomeDesktop.vue
Normal file
133
frontend/src/views/HomeDesktop.vue
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Header from "../components/Header.vue";
|
||||||
|
import InfoHeader from "../components/InfoHeader.vue";
|
||||||
|
import Player from "../components/Player.vue";
|
||||||
|
import SearchPreview from "../components/SearchPreview.vue";
|
||||||
|
import Playlist from "../components/Playlist.vue";
|
||||||
|
import LectureList from "../components/LectureList.vue";
|
||||||
|
import {searchStore} from "../stores/dataStore.ts";
|
||||||
|
|
||||||
|
const search = searchStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<div class="home__left">
|
||||||
|
<InfoHeader/>
|
||||||
|
<Playlist :popup="false" />
|
||||||
|
</div>
|
||||||
|
<div class="home__center">
|
||||||
|
<Header/>
|
||||||
|
<div class="home__center__content">
|
||||||
|
<Transition name="slide-up">
|
||||||
|
<component :is="search.searchQuery == '' ? Player : SearchPreview"/>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home__right">
|
||||||
|
<LectureList :popup="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.home {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
gap: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 400px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
&__center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 0 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: transform 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-up {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-leave-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-leave-from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.popups-enter-active {
|
||||||
|
animation: bounce-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popups-leave-active {
|
||||||
|
animation: bounce-in 0.5s reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce-in {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.15) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
97
frontend/src/views/HomeMobile.vue
Normal file
97
frontend/src/views/HomeMobile.vue
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Header from "../components/Header.vue";
|
||||||
|
import InfoHeader from "../components/InfoHeader.vue";
|
||||||
|
import Player from "../components/Player.vue";
|
||||||
|
import SearchPreview from "../components/SearchPreview.vue";
|
||||||
|
import { lectureListIsOpen, playlistsIsOpen } from "../stores/globalStore.ts";
|
||||||
|
import Playlist from "../components/Playlist.vue";
|
||||||
|
import LectureList from "../components/LectureList.vue";
|
||||||
|
import {searchStore} from "../stores/dataStore.ts";
|
||||||
|
|
||||||
|
const search = searchStore();
|
||||||
|
const lectureList = lectureListIsOpen();
|
||||||
|
const playlists = playlistsIsOpen();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Header />
|
||||||
|
<InfoHeader />
|
||||||
|
<div class="content">
|
||||||
|
<Transition name="slide-up">
|
||||||
|
<component :is="search.searchQuery == '' ? Player : SearchPreview" />
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Transition name="popups">
|
||||||
|
<component :is="playlists.isOpen ? Playlist : ''" />
|
||||||
|
</Transition>
|
||||||
|
<Transition name="popups">
|
||||||
|
<component :is="lectureList.isOpen ? LectureList : ''" />
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 0 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: transform 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-up {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
.slide-up-leave-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-leave-from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
.slide-up-enter-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.popups-enter-active {
|
||||||
|
animation: bounce-in 0.5s;
|
||||||
|
}
|
||||||
|
.popups-leave-active {
|
||||||
|
animation: bounce-in 0.5s reverse;
|
||||||
|
}
|
||||||
|
@keyframes bounce-in {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.15) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
127
frontend/src/views/HomeTablet.vue
Normal file
127
frontend/src/views/HomeTablet.vue
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Header from "../components/Header.vue";
|
||||||
|
import InfoHeader from "../components/InfoHeader.vue";
|
||||||
|
import Player from "../components/Player.vue";
|
||||||
|
import SearchPreview from "../components/SearchPreview.vue";
|
||||||
|
import { lectureListIsOpen} from "../stores/globalStore.ts";
|
||||||
|
import Playlist from "../components/Playlist.vue";
|
||||||
|
import LectureList from "../components/LectureList.vue";
|
||||||
|
import {searchStore} from "../stores/dataStore.ts";
|
||||||
|
|
||||||
|
const search = searchStore();
|
||||||
|
const lectureList = lectureListIsOpen();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<div class="home__left">
|
||||||
|
<InfoHeader/>
|
||||||
|
<Playlist :popup="false" />
|
||||||
|
</div>
|
||||||
|
<div class="home__center">
|
||||||
|
<Header/>
|
||||||
|
<div class="home__center__content">
|
||||||
|
<Transition name="slide-up">
|
||||||
|
<component :is="search.searchQuery == '' ? Player : SearchPreview"/>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
<Transition name="popups">
|
||||||
|
<component :is="lectureList.isOpen ? LectureList : ''"/>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.home {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
gap: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 0 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: transform 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-up {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-leave-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-leave-from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.popups-enter-active {
|
||||||
|
animation: bounce-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popups-leave-active {
|
||||||
|
animation: bounce-in 0.5s reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce-in {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.15) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateX(-50%);
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user