Initial commit
20
backend/app.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import chatRouter from './routers/chatRouter.js';
|
||||
import bodyParser from 'body-parser';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use('/api/chat', chatRouter);
|
||||
app.use('/images', express.static(path.join(__dirname, '../frontend/public/images')));
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '../frontend/dist/index.html'));
|
||||
});
|
||||
|
||||
export default app;
|
||||
43
backend/controllers/chatController.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { getMessages as getMessagesService, findMatch, registerUser as registerUserService, addMessage, endChat } from '../services/chatService.js';
|
||||
|
||||
export const getMessages = (req, res) => {
|
||||
const { to, from } = req.body;
|
||||
const messages = getMessagesService(to, from);
|
||||
res.status(200).json(messages);
|
||||
};
|
||||
|
||||
export const findRandomChatMatch = (req, res) => {
|
||||
const { genders, age, id } = req.body;
|
||||
const match = findMatch(genders, age, id);
|
||||
if (match) {
|
||||
res.status(200).json({ status: 'matched', user: match });
|
||||
} else {
|
||||
res.status(200).json({ status: 'waiting' });
|
||||
}
|
||||
};
|
||||
|
||||
export const registerUser = (req, res) => {
|
||||
const { gender, age } = req.body;
|
||||
const userId = registerUserService(gender, age);
|
||||
res.status(200).json({ id: userId });
|
||||
};
|
||||
|
||||
export const sendMessage = (req, res) => {
|
||||
const from = req.body.from;
|
||||
const to = req.body.to;
|
||||
const text = req.body.text;
|
||||
const message = addMessage(from, to, text);
|
||||
res.status(200).json(message);
|
||||
};
|
||||
|
||||
export const removeUser = (req, res) => {
|
||||
const { id } = req.body;
|
||||
removeUserService(id);
|
||||
res.sendStatus(200);
|
||||
};
|
||||
|
||||
export const stopChat = (req, res) => {
|
||||
const { id } = req.body;
|
||||
endChat(id);
|
||||
res.sendStatus(200);
|
||||
}
|
||||
1
backend/dist/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>YourPart</title><script defer="defer" src="/js/chunk-vendors.e0723df7.js"></script><script defer="defer" src="/js/app.989c1f98.js"></script></head><body><div id="app"></div></body></html>
|
||||
2
backend/dist/js/app.989c1f98.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(()=>{"use strict";var e={855:(e,t,n)=>{var s=n(756),r=n(641),o=n(33),a=n(751);function i(e,t,n,s,i,u){return(0,r.uX)(),(0,r.CE)("div",null,[((0,r.uX)(!0),(0,r.CE)(r.FK,null,(0,r.pI)(i.messages,(e=>((0,r.uX)(),(0,r.CE)("div",{key:e.id},(0,o.v_)(e.text),1)))),128)),(0,r.bo)((0,r.Lk)("input",{"onUpdate:modelValue":t[0]||(t[0]=e=>i.newMessage=e),onKeyup:t[1]||(t[1]=(0,a.jR)(((...e)=>u.sendMessage&&u.sendMessage(...e)),["enter"]))},null,544),[[a.Jo,i.newMessage]])])}var u=n(373);const l={data(){return{socket:null,messages:[],newMessage:""}},created(){this.socket=(0,u.Ay)("http://localhost:3001"),this.socket.on("newMessage",(e=>{this.messages.push(e)})),fetch("/api/chat/messages").then((e=>e.json())).then((e=>{this.messages=e}))},methods:{sendMessage(){if(""!==this.newMessage.trim()){const e={id:Date.now(),text:this.newMessage};this.socket.emit("newMessage",e),this.newMessage=""}}}};var d=n(262);const c=(0,d.A)(l,[["render",i]]),f=c;s["default"].config.productionTip=!1,new s["default"]({render:e=>e(f)}).$mount("#app")}},t={};function n(s){var r=t[s];if(void 0!==r)return r.exports;var o=t[s]={exports:{}};return e[s](o,o.exports,n),o.exports}n.m=e,(()=>{var e=[];n.O=(t,s,r,o)=>{if(!s){var a=1/0;for(d=0;d<e.length;d++){for(var[s,r,o]=e[d],i=!0,u=0;u<s.length;u++)(!1&o||a>=o)&&Object.keys(n.O).every((e=>n.O[e](s[u])))?s.splice(u--,1):(i=!1,o<a&&(a=o));if(i){e.splice(d--,1);var l=r();void 0!==l&&(t=l)}}return t}o=o||0;for(var d=e.length;d>0&&e[d-1][2]>o;d--)e[d]=e[d-1];e[d]=[s,r,o]}})(),(()=>{n.d=(e,t)=>{for(var s in t)n.o(t,s)&&!n.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})}})(),(()=>{n.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}()})(),(()=>{n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})(),(()=>{n.r=e=>{"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}})(),(()=>{var e={524:0};n.O.j=t=>0===e[t];var t=(t,s)=>{var r,o,[a,i,u]=s,l=0;if(a.some((t=>0!==e[t]))){for(r in i)n.o(i,r)&&(n.m[r]=i[r]);if(u)var d=u(n)}for(t&&t(s);l<a.length;l++)o=a[l],n.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return n.O(d)},s=self["webpackChunkfrontend"]=self["webpackChunkfrontend"]||[];s.forEach(t.bind(null,0)),s.push=t.bind(null,s.push.bind(s))})();var s=n.O(void 0,[504],(()=>n(855)));s=n.O(s)})();
|
||||
//# sourceMappingURL=app.989c1f98.js.map
|
||||
1
backend/dist/js/app.989c1f98.js.map
vendored
Normal file
23
backend/dist/js/chunk-vendors.e0723df7.js
vendored
Normal file
1
backend/dist/js/chunk-vendors.e0723df7.js.map
vendored
Normal file
1026
backend/package-lock.json
generated
Normal file
19
backend/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"amqplib": "^0.10.4",
|
||||
"express": "^4.19.2",
|
||||
"socket.io": "^4.7.5",
|
||||
"uuid": "^10.0.0"
|
||||
}
|
||||
}
|
||||
14
backend/routers/chatRouter.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Router } from 'express';
|
||||
import { getMessages, findRandomChatMatch, registerUser, sendMessage, stopChat, removeUser } from '../controllers/chatController.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/messages', getMessages);
|
||||
router.post('/findMatch', findRandomChatMatch);
|
||||
router.post('/register', registerUser);
|
||||
router.post('/sendMessage', sendMessage);
|
||||
router.post('/getMessages', getMessages);
|
||||
router.post('/leave', stopChat);
|
||||
router.post('/exit', removeUser);
|
||||
|
||||
export default router;
|
||||
55
backend/server.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import http from 'http';
|
||||
import { Server } from 'socket.io';
|
||||
import amqp from 'amqplib/callback_api.js';
|
||||
import app from './app.js';
|
||||
import path from 'path';
|
||||
import express from 'express';
|
||||
|
||||
const server = http.createServer(app);
|
||||
const io = new Server(server);
|
||||
|
||||
const RABBITMQ_URL = 'amqp://localhost';
|
||||
const QUEUE = 'chat_messages';
|
||||
|
||||
const __dirname = path.resolve();
|
||||
const frontendPath = path.join(__dirname, 'path/to/your/frontend/build/folder');
|
||||
app.use(express.static(frontendPath));
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(frontendPath, 'index.html'));
|
||||
});
|
||||
|
||||
amqp.connect(RABBITMQ_URL, (err, connection) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
connection.createChannel((err, channel) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
channel.assertQueue(QUEUE, { durable: false });
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
console.log('A user connected');
|
||||
|
||||
channel.consume(QUEUE, (msg) => {
|
||||
const message = JSON.parse(msg.content.toString());
|
||||
io.emit('newMessage', message);
|
||||
}, { noAck: true });
|
||||
|
||||
socket.on('newMessage', (message) => {
|
||||
channel.sendToQueue(QUEUE, Buffer.from(JSON.stringify(message)));
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log('A user disconnected');
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(3001, () => {
|
||||
console.log('Server is running on port 3001');
|
||||
});
|
||||
});
|
||||
});
|
||||
76
backend/services/chatService.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
let messages = [];
|
||||
let searchQueue = [];
|
||||
let users = [];
|
||||
let currentChats = [];
|
||||
|
||||
export const getMessages = (toId, fromId) => {
|
||||
const userChats = currentChats.filter(chat => chat.includes(toId) && chat.includes(fromId));
|
||||
if (userChats.length === 0) {
|
||||
fromId = '';
|
||||
}
|
||||
const userMessages = messages.filter(message => message.to = toId && ["system", fromId].includes(message.from));
|
||||
messages = messages.filter(message => message.to === toId && ["system", fromId].includes(message.from));
|
||||
return userMessages;
|
||||
};
|
||||
|
||||
export const addMessage = (from, to, text) => {
|
||||
const userChats = currentChats.filter(chat => chat.includes(from) && chat.includes(to));
|
||||
if (userChats.length === 0) {
|
||||
return;
|
||||
}
|
||||
messages.push({ from: from, to: to, text: text });
|
||||
return { text: text };
|
||||
};
|
||||
|
||||
export const findMatch = (genders, age, id) => {
|
||||
const currentUsersChat = currentChats.filter(chat => chat.includes(id));
|
||||
if (currentUsersChat.length > 0) {
|
||||
return findUser(currentUsersChat[0][0] === id ? currentUsersChat[0][1] : currentUsersChat[0][0]);
|
||||
}
|
||||
let filteredSearchQueue = users.filter(user =>
|
||||
searchQueue.some(sq => sq.id === user.id) && user.id !== id
|
||||
&& currentChats.filter(chat => chat.includes(user.id)).length === 0
|
||||
).sort(() => Math.random() - 0.5);
|
||||
for (let i = 0; i < filteredSearchQueue.length; i++) {
|
||||
const user = filteredSearchQueue[i];
|
||||
const ageMatch = user.age >= age.min && user.age <= age.max;
|
||||
const genderMatch = genders.includes(user.gender);
|
||||
if (ageMatch && genderMatch) {
|
||||
for (let j = searchQueue.length - 1; j >= 0; j--) {
|
||||
if ([id, user.id].includes(searchQueue[j].id)) {
|
||||
searchQueue.splice(j, 1);
|
||||
}
|
||||
}
|
||||
currentChats.push([user.id, id]);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
if (!searchQueue.find(user => user.id === id)) {
|
||||
searchQueue.push({ id, genders, age });
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const findUser = (id) => {
|
||||
return users.find(user => user.id === id);
|
||||
};
|
||||
|
||||
export const registerUser = (gender, age) => {
|
||||
const id = uuidv4();
|
||||
users.push({ gender, age, id });
|
||||
return id;
|
||||
};
|
||||
|
||||
export const removeUser = (id) => {
|
||||
searchQueue = searchQueue.filter(user => user.id !== id);
|
||||
users = users.filter(user => user.id !== id);
|
||||
currentChats = currentChats.filter(pair => pair[0] === id || pair[1] === id);
|
||||
messages = messages.filter(message => message.from === id || message.to === id);
|
||||
};
|
||||
|
||||
export const endChat = (userId) => {
|
||||
currentChats = currentChats.filter(chat => !chat.includes(userId));
|
||||
messages.push({ to: userId, from: 'system', activity: 'otheruserleft'})
|
||||
}
|
||||
8008
frontend/package-lock.json
generated
Normal file
21
frontend/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"vue": "~3.4.31",
|
||||
"vue-i18n": "^10.0.0-beta.2",
|
||||
"vue-router": "^4.0.13",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"sass": "^1.77.8",
|
||||
"sass-loader": "^10.5.2"
|
||||
}
|
||||
}
|
||||
BIN
frontend/public/images/backgrounds/welcome.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
frontend/public/images/icons/accept16.png
Normal file
|
After Width: | Height: | Size: 363 B |
BIN
frontend/public/images/icons/accept24.png
Normal file
|
After Width: | Height: | Size: 505 B |
BIN
frontend/public/images/icons/arrowdownblack.png
Normal file
|
After Width: | Height: | Size: 338 B |
BIN
frontend/public/images/icons/arrowdowngray.png
Normal file
|
After Width: | Height: | Size: 325 B |
BIN
frontend/public/images/icons/ban16.png
Normal file
|
After Width: | Height: | Size: 441 B |
BIN
frontend/public/images/icons/ban24.png
Normal file
|
After Width: | Height: | Size: 681 B |
BIN
frontend/public/images/icons/block16.png
Normal file
|
After Width: | Height: | Size: 496 B |
BIN
frontend/public/images/icons/block24.png
Normal file
|
After Width: | Height: | Size: 751 B |
BIN
frontend/public/images/icons/calendar.png
Normal file
|
After Width: | Height: | Size: 508 B |
BIN
frontend/public/images/icons/cam16.png
Normal file
|
After Width: | Height: | Size: 416 B |
BIN
frontend/public/images/icons/cam24.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
frontend/public/images/icons/chat.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/public/images/icons/chaticon16.png
Normal file
|
After Width: | Height: | Size: 501 B |
BIN
frontend/public/images/icons/chaticon24.png
Normal file
|
After Width: | Height: | Size: 739 B |
BIN
frontend/public/images/icons/cloud.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
frontend/public/images/icons/cloud16.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
frontend/public/images/icons/cloud24.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
frontend/public/images/icons/decline16.png
Normal file
|
After Width: | Height: | Size: 500 B |
BIN
frontend/public/images/icons/decline24.png
Normal file
|
After Width: | Height: | Size: 723 B |
BIN
frontend/public/images/icons/dice16.png
Normal file
|
After Width: | Height: | Size: 570 B |
BIN
frontend/public/images/icons/dice24.png
Normal file
|
After Width: | Height: | Size: 823 B |
BIN
frontend/public/images/icons/enter16.png
Normal file
|
After Width: | Height: | Size: 274 B |
BIN
frontend/public/images/icons/enter24.png
Normal file
|
After Width: | Height: | Size: 330 B |
BIN
frontend/public/images/icons/exit16.png
Normal file
|
After Width: | Height: | Size: 394 B |
BIN
frontend/public/images/icons/exit24.png
Normal file
|
After Width: | Height: | Size: 520 B |
BIN
frontend/public/images/icons/falukant.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
frontend/public/images/icons/falukant16.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
frontend/public/images/icons/falukant24.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
frontend/public/images/icons/folder.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
frontend/public/images/icons/friends16.png
Normal file
|
After Width: | Height: | Size: 646 B |
BIN
frontend/public/images/icons/friends24.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/public/images/icons/friendsadd16.png
Normal file
|
After Width: | Height: | Size: 741 B |
BIN
frontend/public/images/icons/friendsadd24.png
Normal file
|
After Width: | Height: | Size: 941 B |
BIN
frontend/public/images/icons/home16.png
Normal file
|
After Width: | Height: | Size: 501 B |
BIN
frontend/public/images/icons/home24.png
Normal file
|
After Width: | Height: | Size: 636 B |
BIN
frontend/public/images/icons/imprint16.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
frontend/public/images/icons/imprint24.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
frontend/public/images/icons/info16.png
Normal file
|
After Width: | Height: | Size: 576 B |
BIN
frontend/public/images/icons/info24.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
frontend/public/images/icons/lock16.png
Normal file
|
After Width: | Height: | Size: 219 B |
BIN
frontend/public/images/icons/lock24.png
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
frontend/public/images/icons/logo_color.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
frontend/public/images/icons/logo_mono.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
frontend/public/images/icons/malefemale16.png
Normal file
|
After Width: | Height: | Size: 527 B |
BIN
frontend/public/images/icons/malefemale24.png
Normal file
|
After Width: | Height: | Size: 786 B |
BIN
frontend/public/images/icons/message16.png
Normal file
|
After Width: | Height: | Size: 380 B |
BIN
frontend/public/images/icons/message24.png
Normal file
|
After Width: | Height: | Size: 493 B |
BIN
frontend/public/images/icons/minigames16.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
frontend/public/images/icons/minigames24.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/public/images/icons/minimize16.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
frontend/public/images/icons/minimize24.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/public/images/icons/privacy.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
frontend/public/images/icons/privacy16.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
frontend/public/images/icons/privacy24.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/public/images/icons/privacy30.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/public/images/icons/privacy45.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
frontend/public/images/icons/profile16.png
Normal file
|
After Width: | Height: | Size: 448 B |
BIN
frontend/public/images/icons/profile24.png
Normal file
|
After Width: | Height: | Size: 599 B |
BIN
frontend/public/images/icons/rain.png
Normal file
|
After Width: | Height: | Size: 645 B |
BIN
frontend/public/images/icons/search16.png
Normal file
|
After Width: | Height: | Size: 643 B |
BIN
frontend/public/images/icons/search24.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
frontend/public/images/icons/settings.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
frontend/public/images/icons/settings16.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
frontend/public/images/icons/settings24.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/public/images/icons/socialnetwork.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
frontend/public/images/icons/sun-clouds.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/public/images/icons/sun-rain.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
frontend/public/images/icons/sun.png
Normal file
|
After Width: | Height: | Size: 808 B |
BIN
frontend/public/images/icons/unban16.png
Normal file
|
After Width: | Height: | Size: 360 B |
BIN
frontend/public/images/icons/unban24.png
Normal file
|
After Width: | Height: | Size: 466 B |
BIN
frontend/public/images/icons/unblock16.png
Normal file
|
After Width: | Height: | Size: 603 B |
BIN
frontend/public/images/icons/unblock24.png
Normal file
|
After Width: | Height: | Size: 1010 B |
BIN
frontend/public/images/icons/usersearch16.png
Normal file
|
After Width: | Height: | Size: 525 B |
BIN
frontend/public/images/icons/usersearch24.png
Normal file
|
After Width: | Height: | Size: 737 B |
BIN
frontend/public/images/logos/logo.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
frontend/public/images/mascot/mascot_female.old.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
frontend/public/images/mascot/mascot_female.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
frontend/public/images/mascot/mascot_male.old.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
frontend/public/images/mascot/mascot_male.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
frontend/public/images/screenshots/friends.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
41
frontend/src/App.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<AppHeader />
|
||||
<AppNavigation v-if="isLoggedIn" />
|
||||
<AppContent />
|
||||
<AppFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import AppHeader from './components/AppHeader.vue';
|
||||
import AppNavigation from './components/AppNavigation.vue';
|
||||
import AppContent from './components/AppContent.vue';
|
||||
import AppFooter from './components/AppFooter.vue';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
mounted() {
|
||||
document.title = 'yourPart';
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isLoggedIn'])
|
||||
},
|
||||
components: {
|
||||
AppHeader,
|
||||
AppNavigation,
|
||||
AppContent,
|
||||
AppFooter
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
49
frontend/src/assets/styles.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 10px;
|
||||
padding: 5px 12px;
|
||||
cursor: pointer;
|
||||
background: #F9A22C;
|
||||
color: #000000;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
transition: background 0.05s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #fdf1db;
|
||||
color: #7E471B;
|
||||
border: 1px solid #7E471B;
|
||||
}
|
||||
|
||||
.rc-system {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.rc-self {
|
||||
color: #ff0000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rc-partner {
|
||||
color: #0000ff;
|
||||
font-weight: bold;
|
||||
}
|
||||
20
frontend/src/components/AppContent.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<main>
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppContent'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
main {
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
110
frontend/src/components/AppFooter.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<footer>
|
||||
<div class="logo"><img src="/images/icons/logo_color.png"></div>
|
||||
<div class="window-bar">
|
||||
<button v-for="dialog in openDialogs" :key="dialog.dialog.name" class="dialog-button"
|
||||
@click="toggleDialogMinimize(dialog.dialog.name)" :title="dialog.dialog.title">
|
||||
<img v-if="dialog.dialog.icon" :src="'/images/icons/' + dialog.dialog.icon" />
|
||||
<span class="button-text">{{ dialog.dialog.isTitleTranslated ? $t(dialog.dialog.title) : dialog.dialog.title
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="static-block">
|
||||
<a href="#" @click.prevent="openImprintDialog">Impressum</a>
|
||||
<a href="#" @click.prevent="openDataPrivacyDialog">Datenschutzerklärung</a>
|
||||
</div>
|
||||
<ImprintDialog ref="imprintDialog" name="imprintDialog" />
|
||||
<DataPrivacyDialog ref="dataPrivacyDialog" name="dataPrivacyDialog" />
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import ImprintDialog from '../dialogues/standard/ImprintDialog.vue';
|
||||
import DataPrivacyDialog from '../dialogues/standard/DataPrivacyDialog.vue';
|
||||
|
||||
export default {
|
||||
name: 'AppFooter',
|
||||
components: {
|
||||
ImprintDialog,
|
||||
DataPrivacyDialog,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('dialogs', ['openDialogs'])
|
||||
},
|
||||
methods: {
|
||||
openImprintDialog() {
|
||||
this.$refs.imprintDialog.open();
|
||||
},
|
||||
openDataPrivacyDialog() {
|
||||
this.$refs.dataPrivacyDialog.open();
|
||||
},
|
||||
toggleDialogMinimize(dialogName) {
|
||||
this.$store.dispatch('dialogs/toggleDialogMinimize', dialogName);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
footer {
|
||||
display: flex;
|
||||
background-color: #7BBE55;
|
||||
height: 38px;
|
||||
width: 100%;
|
||||
color: #7E471B;
|
||||
}
|
||||
|
||||
.logo,
|
||||
.window-bar,
|
||||
.static-block {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.window-bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.dialog-button {
|
||||
max-width: 12em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
height: 1.8em;
|
||||
border: 1px solid #0a4337;
|
||||
box-shadow: 1px 1px 2px #484949;
|
||||
}
|
||||
|
||||
.dialog-button>img {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.button-text {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.logo>img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.static-block {
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
.static-block>a {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
</style>
|
||||
30
frontend/src/components/AppHeader.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<header>
|
||||
<div class="logo"><img src="/images/logos/logo.png" /></div>
|
||||
<div class="advertisement">Advertisement</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppHeader'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.logo, .title, .advertisement {
|
||||
text-align: center;
|
||||
}
|
||||
.advertisement {
|
||||
flex: 1;
|
||||
}
|
||||
.logo > img {
|
||||
max-height: 50px;
|
||||
}
|
||||
</style>
|
||||