Initial commit

This commit is contained in:
2020-09-18 22:11:30 +03:00
commit aaade027b6
17 changed files with 3445 additions and 0 deletions

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# VARIANTS
Веб приложение для распределения вариантов и для деления на бригады

2722
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

37
package.json Normal file
View File

@@ -0,0 +1,37 @@
{
"name": "variancc",
"description": "Variancc",
"version": "0.0.1",
"scripts": {
"dev": "sapper dev",
"build": "sapper build --legacy",
"export": "sapper export --legacy",
"start": "node __sapper__/build"
},
"dependencies": {
"compression": "^1.7.1",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"mongodb": "^3.6.2",
"polka": "next",
"query-string": "^6.13.2",
"sirv": "^1.0.0",
"uuid": "^8.3.0"
},
"devDependencies": {
"sapper": "^0.28.0",
"svelte": "^3.17.3",
"@babel/core": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/runtime": "^7.0.0",
"@rollup/plugin-babel": "^5.0.0",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.0.0",
"@rollup/plugin-replace": "^2.2.0",
"rollup": "^2.3.4",
"rollup-plugin-svelte": "^6.0.0",
"rollup-plugin-terser": "^7.0.0"
}
}

105
rollup.config.js Normal file
View File

@@ -0,0 +1,105 @@
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import commonjs from '@rollup/plugin-commonjs';
import svelte from 'rollup-plugin-svelte';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
import config from 'sapper/config/rollup.js';
import pkg from './package.json';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
const onwarn = (warning, onwarn) =>
(warning.code === 'MISSING_EXPORT' && /'preload'/.test(warning.message)) ||
(warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) ||
onwarn(warning);
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
dev,
hydratable: true,
emitCss: true
}),
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
legacy && babel({
extensions: ['.js', '.mjs', '.html', '.svelte'],
babelHelpers: 'runtime',
exclude: ['node_modules/@babel/**'],
presets: [
['@babel/preset-env', {
targets: '> 0.25%, not dead'
}]
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
['@babel/plugin-transform-runtime', {
useESModules: true
}]
]
}),
!dev && terser({
module: true
})
],
preserveEntrySignatures: false,
onwarn,
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
generate: 'ssr',
hydratable: true,
dev
}),
resolve({
dedupe: ['svelte']
}),
commonjs()
],
external: Object.keys(pkg.dependencies).concat(require('module').builtinModules),
preserveEntrySignatures: 'strict',
onwarn,
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
commonjs(),
!dev && terser()
],
preserveEntrySignatures: false,
onwarn,
}
};

5
src/client.js Normal file
View File

@@ -0,0 +1,5 @@
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')
});

40
src/routes/_error.svelte Normal file
View File

@@ -0,0 +1,40 @@
<script>
export let status;
export let error;
const dev = process.env.NODE_ENV === 'development';
</script>
<style>
h1, p {
margin: 0 auto;
}
h1 {
font-size: 2.8em;
font-weight: 700;
margin: 0 0 0.5em 0;
}
p {
margin: 1em auto;
}
@media (min-width: 480px) {
h1 {
font-size: 4em;
}
}
</style>
<svelte:head>
<title>{status}</title>
</svelte:head>
<h1>{status}</h1>
<p>{error.message}</p>
{#if dev && error.stack}
<pre>{error.stack}</pre>
{/if}

View File

@@ -0,0 +1,3 @@
<main>
<slot></slot>
</main>

164
src/routes/index.svelte Normal file
View File

@@ -0,0 +1,164 @@
<script>
let groupTask = false;
let name = "";
let memberCount = 10;
function createRoom() {
const url=`http://localhost:3000/api/create/?g=${groupTask}&n=${name}&c=${memberCount}`;
window.location.href = url;
}
function connect() {
}
</script>
<svelte:head>
<title>Varian.cc</title>
</svelte:head>
<greeting>
<h1>varian.cc</h1>
<div class="startbtns">
<div>
<h2>Создать работу</h2>
<div class="inputs">
<div class="toggle">
По вариантам
<label class="switch">
<input type="checkbox" bind:checked={groupTask}>
<span class="slider round"></span>
</label>
По бригадам
</div>
<div>
Количество {#if groupTask}бригад{:else}вариантов{/if}:
<input type="number" id="varCount" name="варианты" min="2" max="50" bind:value={memberCount}>
</div>
<div>
Название работы:<br>
<input type="text" size="45" placeholder="Лабораторная работа по физике №3" bind:value={name}>
</div>
<input type="button" value="Создать" on:click={createRoom}>
</div>
</div>
<div>
<h2>Присоедениться к работе</h2>
<div class="inputs">
<div>
Код подключения:<br>
<input type="text" size="45" placeholder="43d0505c-d695-4323-9140-5d7744ec95e7">
</div>
<input type="button" value="Подключиться" on:click={connect}>
</div>
</div>
</div>
</greeting>
<style>
greeting {
text-align: center;
}
.startbtns {
display: flex;
justify-content: space-evenly;
justify-items: center;
}
.startbtns > div {
border-radius: 8px;
box-shadow: lightgrey 0px 5px 20px;
width: 30vw;
height: 30vw;
padding: 2vw;
}
h1 {
font-size: xx-large;
font-weight: 100;
text-transform: uppercase;
}
h2 {
padding-bottom: 1em;
}
.inputs {
display: flex;
height: 75%;
margin-left: 5%;
margin-right: 5%;
flex-direction: column;
justify-content: space-between;
text-align: left;
}
.toggle {
display: flex;
align-items: center;
justify-content: center;
}
.switch {
margin-left: 10px;
margin-right: 10px;
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #2196F3;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #f57323;
}
input:focus + .slider {
box-shadow: 0 0 1px #f57323;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
</style>

100
src/routes/room.svelte Normal file
View File

@@ -0,0 +1,100 @@
<script>
import queryString from "query-string";
let parsed = {};
if (typeof window !== 'undefined') {
parsed = queryString.parse(window.location.search);
}
import { onMount } from 'svelte';
let room;
onMount(async () => {
var url = `http://localhost:3000/api/join/?id=${parsed.id}`;
const res = await fetch(url);
room = await res.json();
console.log(room);
});
function selectVar(selected) {
if (!room.teams[selected]) {
room.teams[selected] = [];
}
room.teams[selected][room.teams[selected].length] = "Толя";
console.log(room.teams[selected]);
}
</script>
<svelte:head>
<title></title>
</svelte:head>
<list>
{#if room}
<h1>{room.name}</h1>
<ol>
{#each room.teams as _, i}
<li class={room.group==="true" ? "group" : "individual"} on:click={() => selectVar(i)}>
{#each room.teams[i] as name}
<span class="name">{name}</span>
{/each}
</li>
{/each}
</ol>
{:else}
Loading...
{/if}
</list>
<style>
h1 {
font-weight: 100;
}
list {
text-align: center;
}
ol {
width: 50%;
margin: auto;
list-style: none;
counter-reset: item;
font-size: larger;
}
li {
text-align: left;
border-bottom: solid lightblue 1px;
counter-increment: item;
padding: 6px;
margin-bottom: 10px;
}
.name {
background-color: lightcoral;
border-radius: 8px;
padding: 3px;
margin-right: 5px;
}
li:before {
margin-right: 10px;
content: counter(item);
border-radius: 100%;
color: white;
width: 1.2em;
text-align: center;
display: inline-block;
}
.individual:before {
background-color: #2196F3;
}
.group:before {
background-color: #f57323;
}
</style>

68
src/server.js Normal file
View File

@@ -0,0 +1,68 @@
import sirv from 'sirv';
import polka from 'polka';
import compression from 'compression';
const dotenv = require('dotenv');
import { v4 as uuidv4 } from 'uuid';
import * as sapper from '@sapper/server';
const MongoClient = require('mongodb');
const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';
dotenv.config();
const app = polka();
MongoClient.connect(process.env.DB_CONNECTION, { useUnifiedTopology: true })
.then(client => {
console.log('Connected to Database')
const db = client.db('variants');
const rooms = db.collection('rooms');
app.use(function(req, res, next) {
res.redirect = location => {
let str = `Redirecting to ${location}`;
res.writeHead(302, {
Location: location,
'Content-Type': 'text/plain',
'Content-Length': str.length,
});
res.end(str);
};
next();
});
app.get('/api/create/', (req, res) => {
console.log(req.query);
const id = uuidv4();
let teams = [];
for (let i = 0; i < req.query.c; i++) {
teams[i] = [];
}
rooms.insertOne({
"id": id,
"name": req.query.n,
"group": req.query.g,
"memberCount": req.query.c,
"teams": teams
});
res.redirect(`http://localhost:3000/room/?id=${id}`);
});
app.get('/api/join/', (req, res) => {
rooms.findOne({ "id": req.query.id }).then(room => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.write(JSON.stringify(room));
res.end();
});
});
app.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
sapper.middleware()
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
});

82
src/service-worker.js Normal file
View File

@@ -0,0 +1,82 @@
import { timestamp, files, shell, routes } from '@sapper/service-worker';
const ASSETS = `cache${timestamp}`;
// `shell` is an array of all the files generated by the bundler,
// `files` is an array of everything in the `static` directory
const to_cache = shell.concat(files);
const cached = new Set(to_cache);
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(ASSETS)
.then(cache => cache.addAll(to_cache))
.then(() => {
self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(async keys => {
// delete old caches
for (const key of keys) {
if (key !== ASSETS) await caches.delete(key);
}
self.clients.claim();
})
);
});
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET' || event.request.headers.has('range')) return;
const url = new URL(event.request.url);
// don't try to handle e.g. data: URIs
if (!url.protocol.startsWith('http')) return;
// ignore dev server requests
if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
// always serve static files and bundler-generated assets from cache
if (url.host === self.location.host && cached.has(url.pathname)) {
event.respondWith(caches.match(event.request));
return;
}
// for pages, you might want to serve a shell `service-worker-index.html` file,
// which Sapper has generated for you. It's not right for every
// app, but if it's right for yours then uncomment this section
/*
if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
event.respondWith(caches.match('/service-worker-index.html'));
return;
}
*/
if (event.request.cache === 'only-if-cached') return;
// for everything else, try the network first, falling back to
// cache if the user is offline. (If the pages never change, you
// might prefer a cache-first approach to a network-first one.)
event.respondWith(
caches
.open(`offline${timestamp}`)
.then(async cache => {
try {
const response = await fetch(event.request);
cache.put(event.request, response.clone());
return response;
} catch(err) {
const response = await cache.match(event.request);
if (response) return response;
throw err;
}
})
);
});

33
src/template.html Normal file
View File

@@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="theme-color" content="#333333">
%sapper.base%
<link rel="stylesheet" href="global.css">
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
<link rel="icon" type="image/png" href="favicon.png">
<!-- Sapper creates a <script> tag containing `src/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
<!-- Sapper generates a <style> tag containing critical CSS
for the current page. CSS for the rest of the app is
lazily loaded when it precaches secondary pages -->
%sapper.styles%
<!-- This contains the contents of the <svelte:head> component, if
the current page has one -->
%sapper.head%
</head>
<body>
<!-- The application will be rendered inside this element,
because `src/client.js` references it -->
<div id="sapper">%sapper.html%</div>
</body>
</html>

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

63
static/global.css Normal file
View File

@@ -0,0 +1,63 @@
html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
-webkit-padding: 0.4em 0;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}

BIN
static/logo-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
static/logo-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

20
static/manifest.json Normal file
View File

@@ -0,0 +1,20 @@
{
"background_color": "#ffffff",
"theme_color": "#333333",
"name": "TODO",
"short_name": "TODO",
"display": "minimal-ui",
"start_url": "/",
"icons": [
{
"src": "logo-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "logo-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}