merge both repos! atempt 1 by making the file system the same!
This commit is contained in:
parent
badb303ea6
commit
2fe58ee6be
128 changed files with 2320 additions and 4285 deletions
316
src/Confetti.js
316
src/Confetti.js
|
@ -1,316 +0,0 @@
|
|||
export function confettiAnimation() {
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
// Utility functions grouped into a single object
|
||||
const Utils = {
|
||||
// Parse pixel values to numeric values
|
||||
parsePx: (value) => parseFloat(value.replace(/px/, "")),
|
||||
|
||||
// Generate a random number between two values, optionally with a fixed precision
|
||||
getRandomInRange: (min, max, precision = 0) => {
|
||||
const multiplier = Math.pow(10, precision);
|
||||
const randomValue = Math.random() * (max - min) + min;
|
||||
return Math.floor(randomValue * multiplier) / multiplier;
|
||||
},
|
||||
|
||||
// Pick a random item from an array
|
||||
getRandomItem: (array) =>
|
||||
array[Math.floor(Math.random() * array.length)],
|
||||
|
||||
// Scaling factor based on screen width
|
||||
getScaleFactor: () => Math.log(window.innerWidth) / Math.log(1920),
|
||||
|
||||
// Debounce function to limit event firing frequency
|
||||
debounce: (func, delay) => {
|
||||
let timeout;
|
||||
return (...args) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func(...args), delay);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Precomputed constants
|
||||
const DEG_TO_RAD = Math.PI / 180;
|
||||
|
||||
// Centralized configuration for default values
|
||||
const defaultConfettiConfig = {
|
||||
confettiesNumber: 250,
|
||||
confettiRadius: 6,
|
||||
confettiColors: [
|
||||
"#fcf403",
|
||||
"#62fc03",
|
||||
"#f4fc03",
|
||||
"#03e7fc",
|
||||
"#03fca5",
|
||||
"#a503fc",
|
||||
"#fc03ad",
|
||||
"#fc03c2",
|
||||
],
|
||||
emojies: [],
|
||||
svgIcon: null, // Example SVG link
|
||||
};
|
||||
|
||||
// Confetti class representing individual confetti pieces
|
||||
class Confetti {
|
||||
constructor({
|
||||
initialPosition,
|
||||
direction,
|
||||
radius,
|
||||
colors,
|
||||
emojis,
|
||||
svgIcon,
|
||||
}) {
|
||||
const speedFactor =
|
||||
Utils.getRandomInRange(0.9, 1.7, 3) *
|
||||
Utils.getScaleFactor();
|
||||
this.speed = { x: speedFactor, y: speedFactor };
|
||||
this.finalSpeedX = Utils.getRandomInRange(0.2, 0.6, 3);
|
||||
this.rotationSpeed =
|
||||
emojis.length || svgIcon
|
||||
? 0.01
|
||||
: Utils.getRandomInRange(0.03, 0.07, 3) *
|
||||
Utils.getScaleFactor();
|
||||
this.dragCoefficient = Utils.getRandomInRange(
|
||||
0.0005,
|
||||
0.0009,
|
||||
6
|
||||
);
|
||||
this.radius = { x: radius, y: radius };
|
||||
this.initialRadius = radius;
|
||||
this.rotationAngle =
|
||||
direction === "left"
|
||||
? Utils.getRandomInRange(0, 0.2, 3)
|
||||
: Utils.getRandomInRange(-0.2, 0, 3);
|
||||
this.emojiRotationAngle = Utils.getRandomInRange(
|
||||
0,
|
||||
2 * Math.PI
|
||||
);
|
||||
this.radiusYDirection = "down";
|
||||
|
||||
const angle =
|
||||
direction === "left"
|
||||
? Utils.getRandomInRange(82, 15) * DEG_TO_RAD
|
||||
: Utils.getRandomInRange(-15, -82) * DEG_TO_RAD;
|
||||
this.absCos = Math.abs(Math.cos(angle));
|
||||
this.absSin = Math.abs(Math.sin(angle));
|
||||
|
||||
const offset = Utils.getRandomInRange(-150, 0);
|
||||
const position = {
|
||||
x:
|
||||
initialPosition.x +
|
||||
(direction === "left" ? -offset : offset) * this.absCos,
|
||||
y: initialPosition.y - offset * this.absSin,
|
||||
};
|
||||
|
||||
this.position = { ...position };
|
||||
this.initialPosition = { ...position };
|
||||
this.color =
|
||||
emojis.length || svgIcon
|
||||
? null
|
||||
: Utils.getRandomItem(colors);
|
||||
this.emoji = emojis.length ? Utils.getRandomItem(emojis) : null;
|
||||
this.svgIcon = null;
|
||||
|
||||
// Preload SVG if provided
|
||||
if (svgIcon) {
|
||||
this.svgImage = new Image();
|
||||
this.svgImage.src = svgIcon;
|
||||
this.svgImage.onload = () => {
|
||||
this.svgIcon = this.svgImage; // Mark as ready once loaded
|
||||
};
|
||||
}
|
||||
|
||||
this.createdAt = Date.now();
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
draw(context) {
|
||||
const { x, y } = this.position;
|
||||
const { x: radiusX, y: radiusY } = this.radius;
|
||||
const scale = window.devicePixelRatio;
|
||||
|
||||
if (this.svgIcon) {
|
||||
context.save();
|
||||
context.translate(scale * x, scale * y);
|
||||
context.rotate(this.emojiRotationAngle);
|
||||
context.drawImage(
|
||||
this.svgIcon,
|
||||
-radiusX,
|
||||
-radiusY,
|
||||
radiusX * 2,
|
||||
radiusY * 2
|
||||
);
|
||||
context.restore();
|
||||
} else if (this.color) {
|
||||
context.fillStyle = this.color;
|
||||
context.beginPath();
|
||||
context.ellipse(
|
||||
x * scale,
|
||||
y * scale,
|
||||
radiusX * scale,
|
||||
radiusY * scale,
|
||||
this.rotationAngle,
|
||||
0,
|
||||
2 * Math.PI
|
||||
);
|
||||
context.fill();
|
||||
} else if (this.emoji) {
|
||||
context.font = `${radiusX * scale}px serif`;
|
||||
context.save();
|
||||
context.translate(scale * x, scale * y);
|
||||
context.rotate(this.emojiRotationAngle);
|
||||
context.textAlign = "center";
|
||||
context.fillText(this.emoji, 0, radiusY / 2); // Adjust vertical alignment
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
|
||||
updatePosition(deltaTime, currentTime) {
|
||||
const elapsed = currentTime - this.createdAt;
|
||||
|
||||
if (this.speed.x > this.finalSpeedX) {
|
||||
this.speed.x -= this.dragCoefficient * deltaTime;
|
||||
}
|
||||
|
||||
this.position.x +=
|
||||
this.speed.x *
|
||||
(this.direction === "left" ? -this.absCos : this.absCos) *
|
||||
deltaTime;
|
||||
this.position.y =
|
||||
this.initialPosition.y -
|
||||
this.speed.y * this.absSin * elapsed +
|
||||
(0.00125 * Math.pow(elapsed, 2)) / 2;
|
||||
|
||||
if (!this.emoji && !this.svgIcon) {
|
||||
this.rotationSpeed -= 1e-5 * deltaTime;
|
||||
this.rotationSpeed = Math.max(this.rotationSpeed, 0);
|
||||
|
||||
if (this.radiusYDirection === "down") {
|
||||
this.radius.y -= deltaTime * this.rotationSpeed;
|
||||
if (this.radius.y <= 0) {
|
||||
this.radius.y = 0;
|
||||
this.radiusYDirection = "up";
|
||||
}
|
||||
} else {
|
||||
this.radius.y += deltaTime * this.rotationSpeed;
|
||||
if (this.radius.y >= this.initialRadius) {
|
||||
this.radius.y = this.initialRadius;
|
||||
this.radiusYDirection = "down";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(canvasHeight) {
|
||||
return this.position.y < canvasHeight + 100;
|
||||
}
|
||||
}
|
||||
|
||||
class ConfettiManager {
|
||||
constructor() {
|
||||
this.canvas = document.createElement("canvas");
|
||||
// @ts-ignore
|
||||
this.canvas.style =
|
||||
"position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; pointer-events: none;";
|
||||
document.body.appendChild(this.canvas);
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.confetti = [];
|
||||
this.lastUpdated = Date.now();
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
Utils.debounce(() => this.resizeCanvas(), 200)
|
||||
);
|
||||
this.resizeCanvas();
|
||||
requestAnimationFrame(() => this.loop());
|
||||
}
|
||||
|
||||
resizeCanvas() {
|
||||
this.canvas.width = window.innerWidth * window.devicePixelRatio;
|
||||
this.canvas.height =
|
||||
window.innerHeight * window.devicePixelRatio;
|
||||
}
|
||||
|
||||
addConfetti(config = {}) {
|
||||
const {
|
||||
confettiesNumber,
|
||||
confettiRadius,
|
||||
confettiColors,
|
||||
emojies,
|
||||
svgIcon,
|
||||
} = {
|
||||
...defaultConfettiConfig,
|
||||
...config,
|
||||
};
|
||||
|
||||
const baseY = (5 * window.innerHeight) / 7;
|
||||
for (let i = 0; i < confettiesNumber / 2; i++) {
|
||||
this.confetti.push(
|
||||
new Confetti({
|
||||
initialPosition: { x: 0, y: baseY },
|
||||
direction: "right",
|
||||
radius: confettiRadius,
|
||||
colors: confettiColors,
|
||||
emojis: emojies,
|
||||
svgIcon,
|
||||
})
|
||||
);
|
||||
this.confetti.push(
|
||||
new Confetti({
|
||||
initialPosition: { x: window.innerWidth, y: baseY },
|
||||
direction: "left",
|
||||
radius: confettiRadius,
|
||||
colors: confettiColors,
|
||||
emojis: emojies,
|
||||
svgIcon,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
resetAndStart(config = {}) {
|
||||
// Clear existing confetti
|
||||
this.confetti = [];
|
||||
// Add new confetti
|
||||
this.addConfetti(config);
|
||||
}
|
||||
|
||||
loop() {
|
||||
const currentTime = Date.now();
|
||||
const deltaTime = currentTime - this.lastUpdated;
|
||||
this.lastUpdated = currentTime;
|
||||
|
||||
this.context.clearRect(
|
||||
0,
|
||||
0,
|
||||
this.canvas.width,
|
||||
this.canvas.height
|
||||
);
|
||||
|
||||
this.confetti = this.confetti.filter((item) => {
|
||||
item.updatePosition(deltaTime, currentTime);
|
||||
item.draw(this.context);
|
||||
return item.isVisible(this.canvas.height);
|
||||
});
|
||||
|
||||
requestAnimationFrame(() => this.loop());
|
||||
}
|
||||
}
|
||||
|
||||
const manager = new ConfettiManager();
|
||||
manager.addConfetti();
|
||||
|
||||
const triggerButton = document.getElementById("show-again");
|
||||
if (triggerButton) {
|
||||
triggerButton.addEventListener("click", () =>
|
||||
manager.addConfetti()
|
||||
);
|
||||
}
|
||||
|
||||
const resetInput = document.getElementById("reset");
|
||||
if (resetInput) {
|
||||
resetInput.addEventListener("input", () => manager.resetAndStart());
|
||||
}
|
||||
})();
|
||||
}
|
|
@ -1,348 +0,0 @@
|
|||
<script>
|
||||
import { newTable } from "./timeTable.svelte";
|
||||
|
||||
let table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
|
||||
let TabOpen = false;
|
||||
|
||||
let csv = "";
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div id="wrap">
|
||||
<div id="left">
|
||||
<div class="Header Row">
|
||||
<span class="DayOfWeek" style="Opacity: 0;"></span>
|
||||
{#each table.Times as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Monday</span>
|
||||
{#each table.Monday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Tuesday</span>
|
||||
{#each table.Tuesday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Wednesday</span>
|
||||
{#each table.Wednesday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Thursday</span>
|
||||
{#each table.Thursday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Friday</span>
|
||||
{#each table.Friday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div id="right">
|
||||
<button
|
||||
onclick={() => {
|
||||
table.Times.push("");
|
||||
table.Monday.push("");
|
||||
table.Tuesday.push("");
|
||||
table.Wednesday.push("");
|
||||
table.Thursday.push("");
|
||||
table.Friday.push("");
|
||||
newTable(table);
|
||||
table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
}}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"
|
||||
/></svg
|
||||
>Add new Period
|
||||
</button>
|
||||
<button
|
||||
onclick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"This will remove the last period, it is not reversible! are you sure?"
|
||||
)
|
||||
) {
|
||||
table.Times.pop();
|
||||
table.Monday.pop();
|
||||
table.Tuesday.pop();
|
||||
table.Wednesday.pop();
|
||||
table.Thursday.pop();
|
||||
table.Friday.pop();
|
||||
newTable(table);
|
||||
table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
}
|
||||
}}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
|
||||
/></svg
|
||||
>
|
||||
Delete last Period
|
||||
</button>
|
||||
<button
|
||||
onclick={() => {
|
||||
TabOpen = true;
|
||||
}}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M230-360h120v-60H250v-120h100v-60H230q-17 0-28.5 11.5T190-560v160q0 17 11.5 28.5T230-360Zm156 0h120q17 0 28.5-11.5T546-400v-60q0-17-11.5-31.5T506-506h-60v-34h100v-60H426q-17 0-28.5 11.5T386-560v60q0 17 11.5 30.5T426-456h60v36H386v60Zm264 0h60l70-240h-60l-40 138-40-138h-60l70 240ZM160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h640v-480H160v480Zm0 0v-480 480Z"
|
||||
/></svg
|
||||
>
|
||||
Import From CSV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if TabOpen !== false}
|
||||
<div id="UpperLayer">
|
||||
<div id="wrapClose">
|
||||
<div class="root">
|
||||
<h1>Paste CSV here:</h1>
|
||||
<p></p>
|
||||
<textarea
|
||||
bind:value={csv}
|
||||
id="box"
|
||||
placeholder="Duration 1, Duration 2, Duration 3, Duration 4
|
||||
Subject 01, Subject 02, Subject 03, Subject 04
|
||||
Subject 08, Subject 09, Subject 10, Subject 12
|
||||
Subject 13, Subject 13, Subject 14, Subject 15"
|
||||
></textarea>
|
||||
</div>
|
||||
<div id="options">
|
||||
<button
|
||||
class="close"
|
||||
id="cancel"
|
||||
onclick={() => (TabOpen = false)}
|
||||
aria-label="close">CANCEL</button
|
||||
>
|
||||
<button
|
||||
class="close"
|
||||
id="save"
|
||||
onclick={() => {
|
||||
TabOpen = false;
|
||||
let lines = csv.split("\n");
|
||||
table.Times = lines[0].split(",");
|
||||
table.Monday = lines[1].split(",");
|
||||
table.Tuesday = lines[2].split(",");
|
||||
table.Wednesday = lines[3].split(",");
|
||||
table.Thursday = lines[4].split(",");
|
||||
table.Friday = lines[5].split(",");
|
||||
newTable(table);
|
||||
table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
}}
|
||||
aria-label="close">SAVE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
background-color: #303030;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
background-color: #252525;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.Row {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
background-color: #3f3f3f;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.DayOfWeek {
|
||||
width: 100px;
|
||||
background-color: #30492e;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
background-color: #3f3f3f;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.Header > input {
|
||||
background-color: #30492e;
|
||||
}
|
||||
|
||||
#left {
|
||||
gap: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#right {
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#right > button {
|
||||
background-color: rgb(63, 63, 63);
|
||||
color: white;
|
||||
border: 2px solid white;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
#right > button:hover {
|
||||
transform: scale(1.2);
|
||||
background-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
#UpperLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#wrapClose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close {
|
||||
background-color: #2b2b2b;
|
||||
color: #888;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
}
|
||||
#box {
|
||||
width: 700px;
|
||||
height: 300px;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
resize: vertical;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h1,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#options {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#cancel {
|
||||
background-color: #580000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#save {
|
||||
background-color: #004611;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
|
@ -1,180 +0,0 @@
|
|||
<script module>
|
||||
import Time from "./time.svelte";
|
||||
import TimeTable from "./timeTable.svelte";
|
||||
import EditTimetableDiv from "./EditTimetable.svelte";
|
||||
|
||||
function EditTimetable() {
|
||||
console.log("Edit timetable");
|
||||
TabOpen.v = true;
|
||||
}
|
||||
|
||||
let TabOpen = $state({ v: false });
|
||||
|
||||
export let ShowSeconds = $state({ v: true });
|
||||
</script>
|
||||
|
||||
<div id="wrap">
|
||||
<div id="nav">
|
||||
<a href="#/" aria-label="Back to main menu"
|
||||
><button aria-label="Back to main menu"
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
|
||||
/></svg
|
||||
> GO BACK
|
||||
</span></button
|
||||
></a
|
||||
>
|
||||
<h1>Idle Screen</h1>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => {
|
||||
ShowSeconds.v = !ShowSeconds.v;
|
||||
localStorage.setItem("ShowSeconds", String(ShowSeconds.v));
|
||||
}}
|
||||
><span class="front"
|
||||
>{#if ShowSeconds.v}Disable Seconds{:else}Enable Seconds{/if}</span
|
||||
></button
|
||||
><button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => EditTimetable()}
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v200h-80v-40H200v400h280v80H200Zm0-560h560v-80H200v80Zm0 0v-80 80ZM560-80v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q8 9 12.5 20t4.5 22q0 11-4 22.5T903-300L683-80H560Zm300-263-37-37 37 37ZM620-140h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z"
|
||||
/></svg
|
||||
> Edit timetable
|
||||
</span></button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="root">
|
||||
<TimeTable /><Time />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if TabOpen.v !== false}
|
||||
<div id="UpperLayer">
|
||||
<div id="wrapClose">
|
||||
<EditTimetableDiv />
|
||||
<button
|
||||
class="close"
|
||||
onclick={() => (TabOpen.v = false)}
|
||||
aria-label="close">CLOSE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
#wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
#nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
max-width: 400px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
text-decoration: underline #444;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.5em;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
#UpperLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(5px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#wrapClose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close {
|
||||
background-color: #2b2b2b;
|
||||
color: #888;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
|
@ -1,231 +0,0 @@
|
|||
<script>
|
||||
import { ShowSeconds } from "./main.svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let hour1;
|
||||
let hour2;
|
||||
let min1;
|
||||
let min2;
|
||||
let sec1;
|
||||
let sec2;
|
||||
let AmOrPm;
|
||||
|
||||
function DecideScrool(Object, currentTime, legnth, LastZeroPos) {
|
||||
if (currentTime == 0) {
|
||||
if (Object.scrollTop != 0) {
|
||||
Object.scrollTop = LastZeroPos * legnth;
|
||||
setTimeout(() => {
|
||||
Object.scroll({
|
||||
top: 0,
|
||||
behavior: "instant",
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
Object.scrollTop = currentTime * legnth;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
const now = new Date();
|
||||
|
||||
let hours = now.getHours().toString().padStart(2, "0");
|
||||
|
||||
if (now.getHours() >= 12) {
|
||||
hours = (now.getHours() - 12).toString().padStart(2, "0");
|
||||
AmOrPm = "PM";
|
||||
} else {
|
||||
AmOrPm = "AM";
|
||||
}
|
||||
|
||||
const minutes = now.getMinutes().toString().padStart(2, "0");
|
||||
const seconds = now.getSeconds().toString().padStart(2, "0");
|
||||
|
||||
DecideScrool(hour1, parseInt(hours[0]), 200, 2);
|
||||
DecideScrool(hour2, parseInt(hours[1]), 200, 10);
|
||||
DecideScrool(min1, parseInt(minutes[0]), 200, 6);
|
||||
DecideScrool(min2, parseInt(minutes[1]), 200, 10);
|
||||
if (ShowSeconds) {
|
||||
DecideScrool(sec1, parseInt(seconds[0]), 75, 6);
|
||||
DecideScrool(sec2, parseInt(seconds[1]), 75, 10);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
updateTime();
|
||||
setInterval(updateTime, 1000);
|
||||
});
|
||||
|
||||
let TempLocalStorage = localStorage.getItem("ShowSeconds") || "";
|
||||
|
||||
if (TempLocalStorage == "false") {
|
||||
ShowSeconds.v = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="wrap">
|
||||
<div id="time">
|
||||
<div class="rowOfNumbers" bind:this={hour1}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<div class="rowOfNumbers" bind:this={hour2}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>6</h1>
|
||||
<h1>7</h1>
|
||||
<h1>8</h1>
|
||||
<h1>9</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<h1 id="HourMinDivider">:</h1>
|
||||
<div class="rowOfNumbers" bind:this={min1}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<div class="rowOfNumbers" bind:this={min2}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>6</h1>
|
||||
<h1>7</h1>
|
||||
<h1>8</h1>
|
||||
<h1>9</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
{#if ShowSeconds.v}
|
||||
<h1 id="MinSecDivider">.</h1>
|
||||
<div class="rowOfNumbersSec" bind:this={sec1}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<div class="rowOfNumbersSec" bind:this={sec2}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>6</h1>
|
||||
<h1>7</h1>
|
||||
<h1>8</h1>
|
||||
<h1>9</h1>
|
||||
<h1>0</h1>
|
||||
</div>{/if}
|
||||
<h1 id="AmOrPm">{AmOrPm}</h1>
|
||||
</div>
|
||||
<div id="date">
|
||||
<h2>
|
||||
{new Date().toLocaleString("en-US", { weekday: "short" })}
|
||||
{new Date().getDate()},
|
||||
{new Date().toLocaleString("en-US", { month: "short" })}
|
||||
{new Date().getFullYear()}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
background-color: #252525;
|
||||
padding: 20px 70px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
#time {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: baseline;
|
||||
margin: -30px;
|
||||
}
|
||||
|
||||
#HourMinDivider {
|
||||
font-size: 200px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#MinSecDivider {
|
||||
font-size: 75px;
|
||||
margin: 0;
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
.rowOfNumbers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 200px;
|
||||
overflow-y: hidden;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.rowOfNumbers h1 {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rowOfNumbers > h1 {
|
||||
font-size: 200px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rowOfNumbersSec {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 75px;
|
||||
overflow-y: hidden;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.rowOfNumbersSec > h1 {
|
||||
height: 75px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rowOfNumbersSec > h1 {
|
||||
font-size: 75px;
|
||||
margin: 0;
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
#AmOrPm {
|
||||
font-size: 75px;
|
||||
margin: 0;
|
||||
margin-left: 30px;
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 40px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -1,198 +0,0 @@
|
|||
<script module>
|
||||
export function newTable(timetable) {
|
||||
localStorage.setItem("TimeTable", JSON.stringify(timetable));
|
||||
table = timetable;
|
||||
}
|
||||
|
||||
let TemplateTable = {
|
||||
Times: [
|
||||
"07:50 - 08:50",
|
||||
"08:50 - 09:40",
|
||||
"09:40 - 10:30 ",
|
||||
"10:30 - 11:00",
|
||||
"11:00 - 12:00",
|
||||
"12:00 - 01:00",
|
||||
"01:00 - 02:00",
|
||||
],
|
||||
Monday: [
|
||||
"English",
|
||||
"Sanskrit",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Tuesday: [
|
||||
"English",
|
||||
"Art & Craft",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Wednesday: [
|
||||
"English",
|
||||
"GK",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Thursday: [
|
||||
"English",
|
||||
"Sanskrit",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Friday: [
|
||||
"English",
|
||||
"Computers",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
};
|
||||
|
||||
let table = $state();
|
||||
|
||||
let TempTimeTable = localStorage.getItem("TimeTable") || "";
|
||||
|
||||
if (TempTimeTable != "") {
|
||||
table = JSON.parse(TempTimeTable);
|
||||
} else {
|
||||
newTable($state.snapshot(TemplateTable));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="wrap">
|
||||
<div class="Header Row">
|
||||
<span class="DayOfWeek" style="Opacity: 0;"></span>
|
||||
{#each table.Times as time}
|
||||
<span>{time}</span>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 1}
|
||||
<span class="DayOfWeek NavHighlight">Monday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Monday</span>
|
||||
{/if}
|
||||
{#each table.Monday as time}
|
||||
{#if new Date().getDay() == 1}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 2}
|
||||
<span class="DayOfWeek NavHighlight">Tuesday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Tuesday</span>
|
||||
{/if}
|
||||
{#each table.Tuesday as time}
|
||||
{#if new Date().getDay() == 2}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 3}
|
||||
<span class="DayOfWeek NavHighlight">Wednesday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Wednesday</span>
|
||||
{/if}
|
||||
{#each table.Wednesday as time}
|
||||
{#if new Date().getDay() == 3}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 4}
|
||||
<span class="DayOfWeek NavHighlight">Thursday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Thursday</span>
|
||||
{/if}
|
||||
{#each table.Thursday as time}
|
||||
{#if new Date().getDay() == 4}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 5}
|
||||
<span class="DayOfWeek NavHighlight">Friday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Friday</span>
|
||||
{/if}
|
||||
{#each table.Friday as time}
|
||||
{#if new Date().getDay() == 5}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
background-color: #252525;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.Row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
span {
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
background-color: #3f3f3f;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
width: 140px;
|
||||
text-align: center;
|
||||
}
|
||||
.Header > span {
|
||||
background-color: #30492e;
|
||||
}
|
||||
.DayOfWeek {
|
||||
width: 120px;
|
||||
background-color: #30492e;
|
||||
}
|
||||
.highlight {
|
||||
background-color: #707070;
|
||||
display: block;
|
||||
}
|
||||
.NavHighlight {
|
||||
background-color: #3b8235;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
|
@ -1,249 +0,0 @@
|
|||
<div id="root">
|
||||
<img
|
||||
src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/77e7a04f27807b4e0c16bcda09ea222f9e091616_group_18.svg" id="logo"
|
||||
/>
|
||||
<h2>
|
||||
A collection of awesome tools, games, and more — made to be used in any
|
||||
classroom!
|
||||
</h2>
|
||||
<h3>MAIN</h3>
|
||||
|
||||
<div id="items2">
|
||||
<a href="https://kahoot-clone-rezhackxyz.vercel.app/">
|
||||
<button class="button2">
|
||||
<span class="front2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="70px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="70px"
|
||||
fill="#e3e3e3"
|
||||
><path
|
||||
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
|
||||
/></svg
|
||||
>
|
||||
DaKahootClone
|
||||
<p>The best ever kahoot clone.</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
</div>
|
||||
<h3>OTHERS</h3>
|
||||
<div id="items">
|
||||
<a href="#/IdleScreen">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="m612-292 56-56-148-148v-184h-80v216l172 172ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-400Zm0 320q133 0 226.5-93.5T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160Z"
|
||||
/></svg
|
||||
>
|
||||
Timetable
|
||||
<p>Clock included!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
<a href="#/RandomName">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780Zm-455-80h311q-10-20-55.5-35T480-370q-55 0-100.5 15T325-320ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm0-80q17 0 28.5-11.5T520-600q0-17-11.5-28.5T480-640q-17 0-28.5 11.5T440-600q0 17 11.5 28.5T480-560Zm1 240Zm-1-280Z"
|
||||
/></svg
|
||||
>
|
||||
Name selector
|
||||
<p>for any class activity!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
<a href="#/Wordle">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M182-200q-51 0-79-35.5T82-322l42-300q9-60 53.5-99T282-760h396q60 0 104.5 39t53.5 99l42 300q7 51-21 86.5T778-200q-21 0-39-7.5T706-230l-90-90H344l-90 90q-15 15-33 22.5t-39 7.5Zm16-86 114-114h336l114 114q2 2 16 6 11 0 17.5-6.5T800-304l-44-308q-4-29-26-48.5T678-680H282q-30 0-52 19.5T204-612l-44 308q-2 11 4.5 17.5T182-280q2 0 16-6Zm482-154q17 0 28.5-11.5T720-480q0-17-11.5-28.5T680-520q-17 0-28.5 11.5T640-480q0 17 11.5 28.5T680-440Zm-80-120q17 0 28.5-11.5T640-600q0-17-11.5-28.5T600-640q-17 0-28.5 11.5T560-600q0 17 11.5 28.5T600-560ZM310-440h60v-70h70v-60h-70v-70h-60v70h-70v60h70v70Zm170-40Z"
|
||||
/></svg
|
||||
>
|
||||
Wordle
|
||||
<p>Challenge your vocabulary!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
|
||||
<a href="#/announcer">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M720-440v-80h160v80H720Zm48 280-128-96 48-64 128 96-48 64Zm-80-480-48-64 128-96 48 64-128 96ZM200-200v-160h-40q-33 0-56.5-23.5T80-440v-80q0-33 23.5-56.5T160-600h160l200-120v480L320-360h-40v160h-80Zm240-182v-196l-98 58H160v80h182l98 58Zm120 36v-268q27 24 43.5 58.5T620-480q0 41-16.5 75.5T560-346ZM300-480Z"
|
||||
/></svg
|
||||
>
|
||||
Announcer
|
||||
<p>to shout from the speakers!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="BottomBadge">
|
||||
Made by <a href="https://rezhack.xyz" target="_blank">RezHackXYZ</a> for
|
||||
<a href="https://neighborhood.hackclub.com/" target="_blank">Neighborhood</a
|
||||
>!
|
||||
<a href="https://github.com/RezHackXYZ/ClassRoomStuff" target="_blank"
|
||||
>Contribute here</a
|
||||
> if you want.
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
color: #797979;
|
||||
font-size: 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
#items2 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#items {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px 15px;
|
||||
border-radius: 12px;
|
||||
font-size: 1.25rem;
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-7px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.front2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 3px 30px;
|
||||
border-radius: 12px;
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-7px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: #aaaaaa;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.front2 > p {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button:hover .front {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
#BottomBadge {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
background-color: #242424;
|
||||
color: #aaaaaa;
|
||||
margin-bottom: 10px;
|
||||
width: fit-content;
|
||||
align-self: center;
|
||||
padding: 5px;
|
||||
font-size: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
color: #aaaaaa;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 500px;
|
||||
margin-bottom: 20px;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
|
@ -1,182 +0,0 @@
|
|||
<script>
|
||||
let text = $state("");
|
||||
|
||||
function speak() {
|
||||
window.speechSynthesis.speak(
|
||||
Object.assign(new SpeechSynthesisUtterance(text), {
|
||||
rate: 0.5,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
let TempelateCommonAnounce = $state(["Please be quiet"]);
|
||||
|
||||
let CommonAnounce = $state([]);
|
||||
|
||||
export function newNames(Names) {
|
||||
localStorage.setItem("CommonAnounce", JSON.stringify(Names));
|
||||
}
|
||||
|
||||
let TempCommonAnounce = localStorage.getItem("CommonAnounce") || "";
|
||||
|
||||
if (TempCommonAnounce != "") {
|
||||
CommonAnounce = JSON.parse(TempCommonAnounce);
|
||||
} else {
|
||||
CommonAnounce = $state.snapshot(TempelateCommonAnounce);
|
||||
localStorage.setItem("CommonAnounce", JSON.stringify(CommonAnounce));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="wrap">
|
||||
<h1>Most Announced announcements</h1>
|
||||
|
||||
<div id="mostanouncedanounements">
|
||||
{#each CommonAnounce as anouncement, i}
|
||||
<div>
|
||||
<button
|
||||
class="anuncement"
|
||||
onclick={() => {
|
||||
text = anouncement;
|
||||
speak();
|
||||
}}
|
||||
>
|
||||
{anouncement}
|
||||
</button>
|
||||
<button
|
||||
aria-label="Delete anouncement"
|
||||
onclick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to delete this anouncement?"
|
||||
)
|
||||
) {
|
||||
CommonAnounce.splice(i, 1);
|
||||
localStorage.setItem(
|
||||
"CommonAnounce",
|
||||
JSON.stringify(CommonAnounce)
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="15px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="15px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
|
||||
/></svg
|
||||
></button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h1>Or announce something else</h1>
|
||||
<div>
|
||||
<input
|
||||
bind:value={text}
|
||||
placeholder="Type in here what you want to announce"
|
||||
/>
|
||||
<button onclick={speak}>Play</button>
|
||||
</div>
|
||||
{#if text}
|
||||
<button
|
||||
id="new"
|
||||
onclick={() => {
|
||||
CommonAnounce.push(text);
|
||||
text = "";
|
||||
localStorage.setItem(
|
||||
"CommonAnounce",
|
||||
JSON.stringify(CommonAnounce)
|
||||
);
|
||||
}}>Add "{text}" to "Most Announced announcements"</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
color: white;
|
||||
width: 400px;
|
||||
}
|
||||
button {
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #414141;
|
||||
transform: rotateZ(-15deg) scale(1.2);
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #292929;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#mostanouncedanounements {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.anuncement:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.anuncement {
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
#new {
|
||||
width: fit-content;
|
||||
align-self: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#new:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
|
@ -1,100 +0,0 @@
|
|||
<script>
|
||||
import ActualAnnouncer from "./ActualAnnouncer.svelte";
|
||||
</script>
|
||||
|
||||
<div id="wrap">
|
||||
<div id="nav">
|
||||
<a href="#/" aria-label="Back to main menu"
|
||||
><button aria-label="Back to main menu"
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
|
||||
/></svg
|
||||
> GO BACK
|
||||
</span></button
|
||||
></a
|
||||
>
|
||||
<h1>Announcer</h1>
|
||||
</div>
|
||||
<div id="root">
|
||||
<ActualAnnouncer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#wrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
max-width: 400px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
#root {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
text-decoration: underline #444;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.5em;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
</style>
|
15
src/app.html
Normal file
15
src/app.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en" style="height: 100%; margin: 0">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="https://hc-cdn.hel1.your-objectstorage.com/s/v3/4a82e0c815624c7786ca2a5addbcc74487da8940_group_8__2_.svg"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" style="height: 100%; margin: 0">
|
||||
<div style="display: contents; height: 100%">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,79 +0,0 @@
|
|||
<script module>
|
||||
import Router from "svelte-spa-router";
|
||||
import Wordle from "./wordle/game.svelte";
|
||||
import TypeSelector from "./SelectionMenue/TypeSelector.svelte";
|
||||
import IdleScreen from "./IdleScreen/main.svelte";
|
||||
import RandomName from "./RandomName/main.svelte";
|
||||
import Announcer from "./announcer/main.svelte";
|
||||
import { confettiAnimation } from "./Confetti.js";
|
||||
|
||||
let routes = {
|
||||
"/": TypeSelector,
|
||||
"/Wordle": Wordle,
|
||||
"/IdleScreen": IdleScreen,
|
||||
"/RandomName": RandomName,
|
||||
"/announcer": Announcer,
|
||||
};
|
||||
let ShowAlertDiv = $state(false);
|
||||
let ShowAlertText = $state("this Code is bad");
|
||||
let ShowAlertType = $state("Error");
|
||||
|
||||
export function ShowAlert(text, type) {
|
||||
ShowAlertDiv = true;
|
||||
ShowAlertType = type;
|
||||
|
||||
ShowAlertText = text;
|
||||
setTimeout(() => {
|
||||
ShowAlertDiv = false;
|
||||
}, 1500);
|
||||
|
||||
if (type == "success") {
|
||||
confettiAnimation();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<Router {routes} />
|
||||
</div>
|
||||
{#if ShowAlertDiv == true}
|
||||
<div id="alert">
|
||||
<h1 class={ShowAlertType}>{ShowAlertText}</h1>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
:root {
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family: "Sour Gummy", sans-serif;
|
||||
}
|
||||
|
||||
#alert {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
color: white;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 10px 20px;
|
||||
border-radius: 20px;
|
||||
font-family: "Sour Gummy", sans-serif;
|
||||
}
|
||||
.error {
|
||||
background-color: #830000;
|
||||
}
|
||||
.warning {
|
||||
background-color: #975b00;
|
||||
}
|
||||
.success {
|
||||
background-color: #006b00;
|
||||
}
|
||||
}
|
||||
</style>
|
133
src/lib/config.js
Normal file
133
src/lib/config.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
export let AnswersSymbolAndColorScheme = [
|
||||
{
|
||||
color: "#6E0000",
|
||||
selectedColor: "#AA2222",
|
||||
hoverBorderColor: "#FF5D5D",
|
||||
selectedBorderColor: "#FF0000",
|
||||
symbol: "nf-md-triangle",
|
||||
},
|
||||
{
|
||||
color: "#00316E",
|
||||
selectedColor: "#2255AA",
|
||||
hoverBorderColor: "#5D9CFF",
|
||||
selectedBorderColor: "#0000FF",
|
||||
symbol: "nf-fa-square",
|
||||
},
|
||||
{
|
||||
color: "#6E6E00",
|
||||
selectedColor: "#AAAA22",
|
||||
hoverBorderColor: "#FFFF5D",
|
||||
selectedBorderColor: "#DDFF00",
|
||||
symbol: "nf-fa-circle",
|
||||
},
|
||||
{
|
||||
color: "#006E00",
|
||||
selectedColor: "#22AA22",
|
||||
hoverBorderColor: "#5DFF5D",
|
||||
selectedBorderColor: "#00FF00",
|
||||
symbol: "nf-fa-diamond",
|
||||
},
|
||||
{
|
||||
color: "#4B0082",
|
||||
selectedColor: "#7F33B5",
|
||||
hoverBorderColor: "#B066FF",
|
||||
selectedBorderColor: "#9932CC",
|
||||
symbol: "nf-md-star",
|
||||
},
|
||||
{
|
||||
color: "#FF8C00",
|
||||
selectedColor: "#FFB347",
|
||||
hoverBorderColor: "#FFD580",
|
||||
selectedBorderColor: "#FFA500",
|
||||
symbol: "nf-md-hexagon",
|
||||
},
|
||||
{
|
||||
color: "#008B8B",
|
||||
selectedColor: "#33CCCC",
|
||||
hoverBorderColor: "#66FFFF",
|
||||
selectedBorderColor: "#00CED1",
|
||||
symbol: "nf-md-octagon",
|
||||
},
|
||||
{
|
||||
color: "#8B4513",
|
||||
selectedColor: "#CD853F",
|
||||
hoverBorderColor: "#DEB887",
|
||||
selectedBorderColor: "#A0522D",
|
||||
symbol: "nf-md-heart",
|
||||
},
|
||||
];
|
||||
|
||||
export let DefaultQuestions = [
|
||||
{
|
||||
name: "What should you do when you're free?",
|
||||
answers: ["Do something in real life!", "Play video games", "Code!", "Touch grass!"],
|
||||
correctAnswer: 2,
|
||||
},
|
||||
{
|
||||
name: "Is RezHackXYZ the best programmer in the world?",
|
||||
answers: ["Yes :)", "No :("],
|
||||
correctAnswer: 0,
|
||||
},
|
||||
{
|
||||
name: "Best place in the world?",
|
||||
answers: [
|
||||
"Google",
|
||||
"Microsoft",
|
||||
"Apple",
|
||||
"Samsung",
|
||||
"Hack Club!! :D",
|
||||
"Amazon",
|
||||
"Facebook",
|
||||
"Twitter",
|
||||
],
|
||||
correctAnswer: 4,
|
||||
},
|
||||
];
|
||||
|
||||
export let AiPrompts = {
|
||||
GenerateQuestionsUsingAI: `
|
||||
You are the AI of a quiz game.
|
||||
Generate a list of quiz questions with possible answers and the correct answer index.
|
||||
Each question must have:
|
||||
- A "name" (question text)
|
||||
- An "answers" array (minimum 2, maximum 8 options)
|
||||
- A "correctAnswer" (index starting from 0)
|
||||
Ensure the questions are diverse.
|
||||
Example format:
|
||||
{
|
||||
"name": "What is the capital of France?",
|
||||
"answers": [
|
||||
"Paris",
|
||||
"London",
|
||||
"Berlin",
|
||||
"Madrid"
|
||||
],
|
||||
"correctAnswer": 0
|
||||
}
|
||||
JUST PROVIDE THE JSON AND NOTHING ELSE.
|
||||
|
||||
The user's topic of interest is:
|
||||
[topic]`,
|
||||
GenerateOptionsUsingAI: `
|
||||
You are the AI of a quiz game.
|
||||
Generate a list of answers relevant to the Question the correct answer index.
|
||||
generate 2 things for the question:
|
||||
- An "answers" array (minimum 2, maximum 8 options)
|
||||
- A "correctAnswer" (index starting from 0)
|
||||
Ensure the questions are diverse.
|
||||
Example format if the question is "What is the capital of France?":
|
||||
{
|
||||
"answers": [
|
||||
"Paris",
|
||||
"London",
|
||||
"Berlin",
|
||||
"Madrid"
|
||||
],
|
||||
"correctAnswer": 0
|
||||
}
|
||||
JUST PROVIDE THE JSON AND NOTHING ELSE.
|
||||
|
||||
The user's Question that they want to generate options for is:
|
||||
[question]
|
||||
`
|
||||
};
|
2
src/lib/showAlert.js
Normal file
2
src/lib/showAlert.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import JSConfetti from "js-confetti";
|
||||
const jsConfetti = new JSConfetti();
|
6
src/lib/supabase.js
Normal file
6
src/lib/supabase.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
export const supabase = createClient(
|
||||
import.meta.env.VITE_SUPABASE_URL,
|
||||
import.meta.env.VITE_SUPABASE_ANON_KEY
|
||||
);
|
28
src/routes/+HomePage/Galery.svelte
Normal file
28
src/routes/+HomePage/Galery.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<script>
|
||||
import tools from "./tools.json"
|
||||
</script>
|
||||
|
||||
<div class="flex items-center justify-center p-3">
|
||||
<div
|
||||
class="flex flex-wrap justify-center gap-5"
|
||||
>
|
||||
{#each tools as tool}
|
||||
<a href={tool.link} class="flex justify-center">
|
||||
<div class="card flex w-full max-w-xs flex-col items-center text-center">
|
||||
<img
|
||||
class="mb-2 w-full rounded border-2 border-white"
|
||||
src={tool.image}
|
||||
alt="how the tool {tool.name} looks like"
|
||||
/>
|
||||
<div
|
||||
class="flex items-center justify-center text-2xl whitespace-nowrap md:text-3xl lg:text-4xl"
|
||||
>
|
||||
<i class="nf {tool.icon}"></i>
|
||||
<span class="ml-2">{tool.name}</span>
|
||||
</div>
|
||||
<p>{@html tool.description}</p>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
87
src/routes/+HomePage/ascii.svelte
Normal file
87
src/routes/+HomePage/ascii.svelte
Normal file
|
@ -0,0 +1,87 @@
|
|||
<pre class="w-full text-center font-mono text-[4.5px] leading-[0.6] text-gray-500">
|
||||
##########%#% ############%#
|
||||
#%#################% %###################%#%%
|
||||
%%###################### ###########################%
|
||||
########################## %###########################%#
|
||||
%###########################% %#############################%#
|
||||
##############################% ######%##########################
|
||||
%%###############%##%############ %#################################%
|
||||
###############*+=-----=+*%#######% #######+--=+**#####################%
|
||||
%%###########+=-------------*######% %######=--------=-----=*############%
|
||||
############+----------------.######% #######------------------=*###########
|
||||
##########+------------------.######% %#######% ######*----------------::::=##########%
|
||||
%#########=-----------------::.######%############%% %######+--------------::::::-=##########
|
||||
########*-----------------:::.:######################% %######=------------::::::-====*#########
|
||||
%#######*:---------------:::::.-######################## %######=----------::::::-=======*########
|
||||
#######*:::------------::::::-:=########################% #######---------::::::-======+++=########%
|
||||
########=::::----------:::::-==.+#########################% ######*-------::::::-======++++++=#######%
|
||||
#######+==:::::------::...:--==.*#########################% %######+-----:::::.:-=====++++++++=+######%
|
||||
%#######+===-:::::-::...:===--:.:################%########## %######=---::::::=.-==-=+++++++++++-#######%
|
||||
#######*++====::::::. =#######################+++++*######## %%%% %% #%%% #######=-::::::===.####++++++++++++-*####### #%#%###%% #%%####%# %%#%####%%#
|
||||
#######++++====::::..*#######################*++++++==############## #%#####%% %###############::::::====-.#####*++++++++++==##################% %#%############%% %%%%%###############
|
||||
#######+++++=====-..*#######################%**+++++-:##################% %############ #################*::::======--######+++++++++++-###################%% #################### %%%######################%%
|
||||
######*+++++++====.=##############%##########****+++--#####################%%################################+::=======+-=######+++++++++++:######################%#################################################
|
||||
######*++++++++==-.##########################*****++:=#######################################################========+++:+######++++++++**+:#########################################################################
|
||||
######*++++++++++--##########################******+:+#######################################################+=====+++++.######*+++++*****+:##########################################################################
|
||||
######*++++++++++=-##########################*******.*#######################################################====+++++++.######++++*******=-##########################################################################%
|
||||
######*++++++++++==########################%#*******.########################################################==+++++++++=*###*+++*********-=##########***#############################################**+**#############
|
||||
######***++++++++=-########################%#******+.#####*++++++*###############*++++*#########*+===#######*++++++++++++++++++***********.*########*********############********##################*++++++++++##########
|
||||
######****++++++++:#################*+######*******+:####+++++++++++**########%*++++++-#######*+++==-+######*++++++++++++++++************=.#######************#########***********+#######%#+++++=======+++++++*########%
|
||||
#######****+++++++:################***+#####*******=-###++++++++++++++++++####+++++++++=#####++++++++-######*++++++++++++++*************+.+#####**************+*######***********+++*##%**+++=============+++++++########
|
||||
#######*****++++++=+##############****+*####++++***-=##**+++++++++++++++++:##++++++++++-####++++++++++-#####+++++++++++++***************::#####****************=#####*********++++++=*##++==================++++=*#######%
|
||||
#######*******+++++=#############******+*###+++++**:+##***++++++++++++++++.#********+=:.=##+++++++++-: #####+++++++++++****************::######*******+=+*******=###******+++++++++++-##+=========-----=======+++-########
|
||||
#######********+++++=###########******++=###++++++*.*##****+++=-=********=-#********+.=####++++++++::*######+++++++++*****************=.######*+*****-.:==******-###***++++-.:-=+++====#*=====-----------:======+=+#######
|
||||
%######**********+++*+*#######*******++++=##==+++++.##*******-.=*+*******:+#*********-#####++++++++-*######*+++++++***=+************+++-######++++++=.*###******==##++++++:.*###======:#*==---------------+=======-#######%
|
||||
########***************************++++++-=*====++=.##******+.*###*******.###**********####+++++++++=######*+++++*****.==-=*******+++++:######++++++:=####*++++++-#*++++++.*####======:*#------::--------:#=--====:########%
|
||||
#######*************************+++++++=.=+=====+=:##******=-####******+:###**********+####**++++++++*####++++******* ###*=****+++++++-+#####++++++:*####*+++++=:#*+++++=.#####+===--:+#--------#-------.#+----==-*#######%
|
||||
%#######**********************+++++++==::#+======--##******==####******=-####+*********+###+***++++++++###++********+:#####**+++++++===-#####=====+-*####*+++++=:#*++====:#####=-----:+#=------:#+------.**-------=########
|
||||
%#######********************+++++++===- *#=----==:=##+******=####******:+#####+*********=###+*****++++=*##**********=-#####*++++++=====:#####========####++++++:-#*======:####+------.+#=-----:.#*:::---:+#--------########
|
||||
########+****************+++++++====:.+##------=:+##++*****+*##*******:*######+********-####********++-##**********:=######++++=======:#####+========*+=======.*##======--**+-------.*#+--::::.*#:::::::=#=--------#######
|
||||
%########+**************++++++=====:.*###-------.*###++***************:*#######********-+####*********.#***********.*######++=======--:+####*--==============::###===--------------::##*::::::.+#:::::::-##--------.######
|
||||
########%=***********++++++======:.*###*-------.####+++**************=*######++++*****.*####********=:##********** ########======------#####=----------====-.*####---------------:.+###:::::::=#-::::::.##+------.-######
|
||||
##########=+*******+++++++=====-.:#####+-------.#####=++++++++++++++++-####+++++++++*-.###*********+.+##*********=:########====-------:######--------------.=#####+------------::.-####::::::::#=::::::.##%+:---.:#######
|
||||
%#########*=+***+++++++=====-:.=######+::----::######=+++++++++++++++:+###+++++++++-.*###********+.-###-=+****++--#########=---------.######*-----------:.=#######+-------::::..-#####-::::::.#+::::::.*%##*-:..########
|
||||
%##########=-+++++++=====-:.:*#######=::::--:-#######=-=+++++=+++++-.####*+++++++:.*####*******=.=####*+-::--=+:=#########=---------.#######*::-------..+#########*::-:::::...=######=::::::.#*:::::..=#####*+#########
|
||||
############=--======-:..-*##########=:..::.=########*-::::.:=====.+#####====-:.:#######***+=..+###########*=--##########*------::..#########=::::...-#############+:.....:+########+::::::.*#=...--+################
|
||||
%#############+=--::-=*#################*+=##################===.-#######-:..=#########*-:.-+############################----:..:=############*==+*################################*::....:*##*#####################
|
||||
#############################################################*-:#########+*############*+################################:..:=*####################################################*:=+*##########################
|
||||
########################################################################################################################**####################################################################################%
|
||||
#######################%@%##############################################################+++*#######################################*#######################################################################%
|
||||
###################%@@@@@%##############################################**+++########*++++++++###############################+=-::=###############*+-:::*########################################%%###%%
|
||||
################%@@@@@@@@%##################++++*############**+==---------:#######++++++==-:#########################*+=:::::::.=#########*+=-:::::::.######%%%%%#################%%%%#######%
|
||||
##############%@@@@%#%@@@@@%############*+---------###*+=------------------:+######========-:#########*##########*+=-::::::::::::-#####+=-::::::::::::.######%@@@@@@%###########%@@@@@@#######%
|
||||
############%@@@@%%####%@@@@%##########------------.##=------:::::---------:=######========:=########+::-+*#####:::::::::::::::::.###=::::::::::::::::.+#####%@@@@@@@@%#######%@@@@@@@@########
|
||||
##########%@@@@@%########%@@@@%#######::::---::::::.##=:::::::::::::-------:=#####*-======-.+########-::::::-*##:::::::::::::::::.###=:::::::::::::::::=#####%@@@@@@@@@@%###%@@@@@@@@@@#######%
|
||||
#########@@@@@%############%@@@@%####-:::::::::::::.*#=::::::::::::::::----:-#####+-------- #########:::::::::*#:::::::::::::::::.+##+::::::::::::::::::#####%@@%**%@@@@@%#%@@@@@#**@@@########
|
||||
#########%@@@@@%##########%@@@@@@###*::::::::::::::.*#+::::::::::::::::::--::#####=--------.########*::::::::.=#=:::::::::::::::::=##*:::::::::::::::::.#####%@@%****%@@@@@@@@@#****@@@#######%
|
||||
%##########%@@@@@%######%@@@@@@@@###=::::::::::::::.*#*:::::::::::::::::::::.#####--------:-########+::::::::.*#+::::.........:::.:###::::::::::::::::..*####%@@%******%@@@@@%******@@@########
|
||||
%###########@@@@@%###%@@@@@%#@@@###-::::::::::::::.*#*:::::::::::::::......-#####--------.=########-:::::::: ##*::...............+###::::::::::::.....:#####%@@%*******#@@@%*******@@@#######%
|
||||
############@@@@@@@%@@@@%@@%#@@@###:::::::::::..:::*##:::::::::::::::.=+**######+-------:.*########::::::::.:###...........:-=*######=...........:=+*#######%@@%********%@@#*******@@@#######%
|
||||
%##########@@#*%@@@@@%#*@@%#@@@###-:::::::::.=#######+....::::::::::.##########=:----:::.########*::::::::.=###:........=###########+........:#############%@@%********#@@#*******@@@#######%
|
||||
%#########@@#***%@@#***@@%#@@@###-::::::::.:#########**###+::::::::.*#########-:::::::.:########=::::::::.*###=........=#*+=-+#####*.........##*+==#######%@@%********#@@#*******@@@########
|
||||
%########@@#***#@#****@@%#@@@###+:::::::::-##############+::::::::.*#########::::::::.=########-:::::::. ####+........:......+#####:........:.....:######%@@%********%@@#*******@@@#######%
|
||||
########@@@#**#@#***%@@%#@@@####.::::::::-##############*::::::::.+########*::::::::.+########::::::...:####*...::::::::....-#####-.............:.*#####%@@%********%@@#*******@@@%#######
|
||||
########@@@@%*#@#*#@@@@%#%%%####-.:::::::.###############::::::::.=########+:::::::: ########*::::.....=#####::::::::::::::::#####=..........::::.*#####%@@@@%#*****%@@#****%%@@@@#######
|
||||
#########%@@@@%@#@@@@%##########+..::::::.*##############.....::::-########=:::::::::########=::.......*#####=::::::::::::::.#####*:::::..::::::::+#####%@@@@@@@%#**%@@#*#%@@@@@@@#######
|
||||
%##########%@@@@@@@@%############.........-##############:......:::########-:::::::.-########:........ ######+:::--:::::::::.######:::::::::::::::=#####%@@@@@@@@@%*%@@#%@@@@@@@@@#######
|
||||
#########%#%%@@@@%##############-.........##############-........:########::::::::.=#######*.........:######*:------------:.*#####-:::::::::::----#########%%@@@@@@@@@@@@@@@%%##########
|
||||
%############%@%################=.........=#############-.........########::::::::.=#######=.........=#######--------------.+#####=::::::::-----::#############%@@@@@@@@@%##############
|
||||
%##############################=.........-#############=.........########:::::::::=#######:......::.*#######---------...:-=######+--------:...:-*##############%%@@@@@%###############
|
||||
#######################%######:..........#############+.........*#######-:::::::::######-.....::::.########+-------:-###########*--------.*#####################%@@@%################
|
||||
###########################*-::.........#############*:::......+#######=:::::::..:###+-....:::::.=########+---------############--------.########################%#################
|
||||
#%###################%=----:::::::::::.####%########*::::::...+#######*.................::::::: *########*--------:############=-------:+######################################%%
|
||||
%%##################=-----:::::::::::##############::::::::.=########:..............:::::::-:-##########--------.############+-------:=###########%%####################%#%
|
||||
#%########%%######=------::::::::.=##############-::::::::-########*............:::::::--- *##########=-------:+###########*--------:########% #%#################%
|
||||
#%% #######=-------:::::::.###############=---::::::#########-.........:::::::----.-###########+-------:=############--------.#######% ###############%
|
||||
%######=------------:.+###############=-----::::##########:......:::::::-----:.*###########*--------:############--------.*######% %##########%
|
||||
#######+-----------:.+################=--------.###########-...:::::::------:.*#############--------.############=-------:=###### #%####%#
|
||||
%######+---------:.-##################+--------.############=:::::::------:.:*##############--------:*###########+---------######
|
||||
%#######+::::...:=*########## ######*-----::: ##############-:::------:..+################=-------.+###########*-------::######
|
||||
##########*+*#############% #######+..:--=+*################=::::...:=##################+--::..:-#############--::..:-*#####%
|
||||
########################## %###################%#############***#######################=:-+*#################:-=*#########
|
||||
%######################% ###################% %###########################% #########################################%
|
||||
###################### #################%% %#########################%% ####################%##################%
|
||||
#%################# ###############% %######################## %##################% %################%
|
||||
############### %############# ####################%% ################% %###############
|
||||
#%##%%%#% %#######%## %%%###########%%% #############% ###########%%
|
||||
</pre>
|
11
src/routes/+HomePage/footer.svelte
Normal file
11
src/routes/+HomePage/footer.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div class="mt-5 bg-gray-700 p-3 text-center text-2xl">
|
||||
<div>
|
||||
Made By <a href="https://rezhack.xyz" class="text-blue-400 underline">RezHackXYZ</a> for
|
||||
<a href="https://neighborhood.hackclub.com/" class="text-blue-400 underline">Neighborhood</a>. •
|
||||
Source code available
|
||||
<a href="https://neighborhood.hackclub.com/" class="text-blue-400 underline">here</a>.
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
and the "<a href="?ascii" class="text-blue-400 underline">?ascii</a>" flag for a surprise!
|
||||
</div>
|
||||
</div>
|
42
src/routes/+HomePage/landingPage.svelte
Normal file
42
src/routes/+HomePage/landingPage.svelte
Normal file
|
@ -0,0 +1,42 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import Ascii from "./ascii.svelte";
|
||||
import RightCards from "./rightCards.svelte";
|
||||
|
||||
let FlagAscii = false;
|
||||
onMount(() => {
|
||||
FlagAscii = new URLSearchParams(window.location.search).has("ascii");
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between items-center">
|
||||
<div class="flex h-full items-center justify-around w-full">
|
||||
<div class="flex w-[600px] flex-col items-baseline gap-3">
|
||||
{#if FlagAscii == true}
|
||||
<Ascii />
|
||||
{:else}
|
||||
<img
|
||||
src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/77e7a04f27807b4e0c16bcda09ea222f9e091616_group_18.svg"
|
||||
alt="ClassRoomStuff Logo"
|
||||
class="w-[600px]"
|
||||
/>
|
||||
{/if}
|
||||
<h1 class="text-center text-5xl">
|
||||
The
|
||||
<span class="rounded-full bg-blue-800 px-3 py-1 text-3xl">
|
||||
<i class="nf nf-cod-sparkle"></i> ultimate <i class="nf nf-cod-sparkle"></i>
|
||||
</span>
|
||||
classroom
|
||||
<span class="rounded-full bg-green-800 px-3 py-1 text-3xl">
|
||||
<i class="nf nf-cod-tools"></i> toolkit <i class="nf nf-cod-tools"></i>
|
||||
</span> for all the teachers and students needs!
|
||||
</h1>
|
||||
</div>
|
||||
<RightCards />
|
||||
</div>
|
||||
<div class="rounded-full bg-gray-900 px-3 py-1 text-xl text-gray-500 w-fit m-3">
|
||||
<i class="nf-fa-angles_down nf"></i>
|
||||
Scroll to see more tools!
|
||||
<i class="nf-fa-angles_down nf"></i>
|
||||
</div>
|
||||
</div>
|
24
src/routes/+HomePage/rightCards.svelte
Normal file
24
src/routes/+HomePage/rightCards.svelte
Normal file
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import tools from "./tools.json";
|
||||
</script>
|
||||
|
||||
<div class="hidden flex-col gap-3 lg:flex">
|
||||
{#each tools as tool, i}
|
||||
{#if i < 3}
|
||||
<div class="card max-w-[320px]">
|
||||
<div class="flex gap-2 text-4xl">
|
||||
<i class="nf {tool.icon}"></i>
|
||||
<h1>{tool.name}</h1>
|
||||
</div>
|
||||
|
||||
<p>{@html tool.description}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="card max-w-[320px]">
|
||||
<div class="flex gap-2 text-4xl">
|
||||
<i class="nf nf-fa-angles_down"></i>
|
||||
<h1>and more!</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
37
src/routes/+HomePage/tools.json
Normal file
37
src/routes/+HomePage/tools.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
[
|
||||
{
|
||||
"name": "Kahoot Clone",
|
||||
"description": "A Kahoot clone built from scratch with a Postgres DB, AI integration, and more.",
|
||||
"link": "/kahootclone",
|
||||
"icon": "nf-md-chat_question",
|
||||
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
|
||||
},
|
||||
{
|
||||
"name": "Wordle",
|
||||
"description": "with unlimited tries, customizable world lengths and more.",
|
||||
"link": "/wordle",
|
||||
"icon": "nf-md-file_word_box",
|
||||
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
|
||||
},
|
||||
{
|
||||
"name": "Announcer",
|
||||
"description": "To let the have computer <strike>talk</strike> shout on them, if they don't listen to you",
|
||||
"link": "/announcer",
|
||||
"icon": "nf-md-speaker_wireless",
|
||||
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
|
||||
},
|
||||
{
|
||||
"name": "Name Selecter",
|
||||
"description": "to chose any student randomly, with memory of previous names and more.",
|
||||
"link": "/randomname",
|
||||
"icon": "nf-oct-people",
|
||||
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
|
||||
},
|
||||
{
|
||||
"name": "Time&Table",
|
||||
"description": "The perfect idle screen when theres no teacher with the timetable, time and date!",
|
||||
"link": "/randomname",
|
||||
"icon": "nf-cod-table",
|
||||
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
|
||||
}
|
||||
]
|
1
src/routes/+layout.js
Normal file
1
src/routes/+layout.js
Normal file
|
@ -0,0 +1 @@
|
|||
export const prerender = true;
|
9
src/routes/+layout.svelte
Normal file
9
src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
import "./tailwind.css";
|
||||
|
||||
import { Toaster } from "svelte-5-french-toast";
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<Toaster />
|
||||
<div class="h-full font-[Sour_Gummy]">{@render children()}</div>
|
9
src/routes/+page.svelte
Normal file
9
src/routes/+page.svelte
Normal file
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
import LandingPage from "./+HomePage/landingPage.svelte";
|
||||
import Galery from "./+HomePage/Galery.svelte";
|
||||
import Footer from "./+HomePage/footer.svelte";
|
||||
</script>
|
||||
|
||||
<LandingPage />
|
||||
<Galery />
|
||||
<Footer />
|
36
src/routes/IdleScreen/+page.svelte
Normal file
36
src/routes/IdleScreen/+page.svelte
Normal file
|
@ -0,0 +1,36 @@
|
|||
<script module>
|
||||
import { Modal, Content, Trigger } from "sv-popup";
|
||||
|
||||
import Time from "./components/time/DisplayCollsOfTime.svelte";
|
||||
import TimeTable from "./components/timetable/DisplayRowsOfTimetable.svelte";
|
||||
import EditTimetableDiv from "./components/timetable/EditTimetable.svelte";
|
||||
import { colseModal } from "./logic/TimeAndTableData.svelte.js";
|
||||
|
||||
export let ShowSeconds = $state({ v: true });
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-col">
|
||||
<div>
|
||||
<button
|
||||
class="btn"
|
||||
onclick={() => {
|
||||
ShowSeconds.v = !ShowSeconds.v;
|
||||
localStorage.setItem("ShowSeconds", String(ShowSeconds.v));
|
||||
}}
|
||||
>
|
||||
{#if ShowSeconds.v}Disable Seconds{:else}Enable Seconds{/if}</button
|
||||
>
|
||||
<Modal big={true} close={colseModal.v}>
|
||||
<Content>
|
||||
<EditTimetableDiv />
|
||||
</Content>
|
||||
<Trigger>
|
||||
<button class="btn">Edit timetable </button>
|
||||
</Trigger>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col items-center justify-center">
|
||||
<Time /><TimeTable />
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,40 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import Row from "./row.svelte";
|
||||
|
||||
let ShowSeconds;
|
||||
let ampm;
|
||||
|
||||
onMount(() => {
|
||||
ShowSeconds = localStorage.getItem("ShowSeconds") || "true" == "true" ? true : false;
|
||||
setInterval(() => {
|
||||
ampm = new Date().getHours() >= 12 ? "PM" : "AM";
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="m-5 w-fit rounded-lg bg-gray-700 p-3">
|
||||
<div class="flex items-baseline justify-center">
|
||||
<Row type={"hour"} digit={0} />
|
||||
<Row type={"hour"} digit={1} />
|
||||
<h1 class="m-0 text-[200px] leading-[200px]">:</h1>
|
||||
<Row type={"min"} digit={0} />
|
||||
<Row type={"min"} digit={1} />
|
||||
|
||||
{#if ShowSeconds}
|
||||
<h1 class="text-[75px] leading-none text-gray-500">.</h1>
|
||||
|
||||
<Row type={"sec"} digit={0} />
|
||||
<Row type={"sec"} digit={1} />
|
||||
{/if}
|
||||
<h1 class="text-[75px] leading-none text-gray-500 ml-3">{ampm}</h1>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-center text-5xl text-gray-300">
|
||||
{new Date().toLocaleString("en-US", { weekday: "short" })}
|
||||
{new Date().getDate()},
|
||||
{new Date().toLocaleString("en-US", { month: "short" })}
|
||||
{new Date().getFullYear()}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
11
src/routes/IdleScreen/components/time/digit.svelte
Normal file
11
src/routes/IdleScreen/components/time/digit.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script>
|
||||
let props = $props();
|
||||
</script>
|
||||
|
||||
{#if props.size == "small"}
|
||||
<h1 class="flex items-center justify-center text-[75px] leading-none m-0 text-gray-500">
|
||||
{props.digit}
|
||||
</h1>
|
||||
{:else if props.size == "large"}
|
||||
<h1 class="flex items-center justify-center text-[200px] leading-none m-0">{props.digit}</h1>
|
||||
{/if}
|
29
src/routes/IdleScreen/components/time/row.svelte
Normal file
29
src/routes/IdleScreen/components/time/row.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import Digit from "./digit.svelte";
|
||||
import { updateTime } from "../../logic/updateTime.js";
|
||||
|
||||
let props = $props();
|
||||
let size = props.type == "sec" ? "small" : "large";
|
||||
let digit = props.digit;
|
||||
let digits = digit == 1 ? 10 : digit == 0 && props.type == "hour" ? 2 : 6;
|
||||
|
||||
let thisRow;
|
||||
|
||||
onMount(() => {
|
||||
setInterval(() => {
|
||||
updateTime(thisRow, digit, props.type);
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
style="--height: {size == 'small' ? '75px' : '200px'};"
|
||||
class="flex h-(--height) flex-col overflow-y-hidden scroll-smooth"
|
||||
bind:this={thisRow}
|
||||
>
|
||||
{#each Array(digits) as _, i}
|
||||
<Digit {size} digit={i} />
|
||||
{/each}
|
||||
<Digit {size} digit={0} />
|
||||
</div>
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { timetableData } from "../../logic/TimeAndTableData.svelte";
|
||||
|
||||
let legend = ["", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
||||
|
||||
onMount(() => {
|
||||
timetableData.v = JSON.parse(localStorage.getItem("timetableData")) || timetableData.v;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="m-5 grid grid-cols-[auto_repeat(var(--NoOfPeriods),_auto)] gap-1 gap-x-1 gap-y-2 rounded-lg bg-gray-800 p-3"
|
||||
style="--NoOfPeriods: {timetableData.v[0].length - 1};"
|
||||
>
|
||||
{#each timetableData.v as row, RowIndex}
|
||||
{#each row as time, timeIndex}
|
||||
{#if RowIndex == 0 && timeIndex == 0}
|
||||
<span class="rounded-xl bg-transparent p-1.5 text-center text-xl"></span>
|
||||
{:else if RowIndex == 0}
|
||||
<span class="rounded-xl bg-blue-800 p-1.5 text-center text-xl">{time}</span>
|
||||
{:else if RowIndex == new Date().getDay() && timeIndex == 0}
|
||||
<span class="rounded-xl bg-green-600 p-1.5 text-center text-xl"
|
||||
>{legend[RowIndex]}</span
|
||||
>
|
||||
{:else if RowIndex == new Date().getDay()}
|
||||
<span class="rounded-xl bg-green-600 p-1.5 text-center text-xl">{time}</span>
|
||||
{:else if timeIndex == 0}
|
||||
<span class="rounded-xl bg-green-900 p-1.5 text-center text-xl"
|
||||
>{legend[RowIndex]}</span
|
||||
>
|
||||
{:else}
|
||||
<span class="rounded-xl bg-gray-700 p-1.5 text-center text-xl">{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
|
@ -0,0 +1,58 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { timetableData } from "../../logic/TimeAndTableData.svelte";
|
||||
|
||||
let data = $state.snapshot(timetableData).v;
|
||||
|
||||
let legend = ["", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
||||
|
||||
import { colseModal } from "../../logic/TimeAndTableData.svelte.js";
|
||||
|
||||
onMount(() => {
|
||||
colseModal.v = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="m-5 flex flex-col items-center justify-center rounded-lg bg-gray-800 p-3">
|
||||
<div
|
||||
class=" flex flex-col gap-1 gap-x-1 gap-y-2 overflow-x-auto"
|
||||
style="--NoOfPeriods: {timetableData.v[0].length - 1};"
|
||||
>
|
||||
{#each data as row, RowIndex}
|
||||
<div class="flex">
|
||||
{#each row as _, timeIndex}
|
||||
{#if RowIndex == 0 && timeIndex == 0}
|
||||
<input class="rounded-xl bg-transparent p-0.5 text-center" disabled />
|
||||
{:else if timeIndex == 0}
|
||||
<input
|
||||
class="rounded-xl bg-green-900 p-0.5 text-center"
|
||||
bind:value={legend[RowIndex]}
|
||||
disabled
|
||||
/>
|
||||
{:else if RowIndex == 0}
|
||||
<input
|
||||
class="rounded-xl border-2 border-white bg-blue-800 p-0.5 text-center"
|
||||
bind:value={data[RowIndex][timeIndex]}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
class="rounded-xl border-2 border-white bg-gray-700 p-0.5 text-center"
|
||||
bind:value={data[RowIndex][timeIndex]}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
onclick={() => {
|
||||
timetableData.v = data;
|
||||
localStorage.setItem("timetableData", JSON.stringify(data));
|
||||
colseModal.v = true;
|
||||
}}
|
||||
class="btn green mt-3">SAVE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
20
src/routes/IdleScreen/logic/TimeAndTableData.svelte.js
Normal file
20
src/routes/IdleScreen/logic/TimeAndTableData.svelte.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
export let timetableData = $state({
|
||||
v: [
|
||||
[
|
||||
"07:50 - 08:50",
|
||||
"08:50 - 09:40",
|
||||
"09:40 - 10:30 ",
|
||||
"10:30 - 11:00",
|
||||
"11:00 - 12:00",
|
||||
"12:00 - 01:00",
|
||||
"01:00 - 02:00",
|
||||
],
|
||||
["English", "Sanskrit", "Math", "Lunch", "Hindi", "Social Science", "Science"],
|
||||
["English", "Art & Craft", "Math", "Lunch", "Hindi", "Social Science", "Science"],
|
||||
["English", "GK", "Math", "Lunch", "Hindi", "Social Science", "Science"],
|
||||
["English", "Sanskrit", "Math", "Lunch", "Hindi", "Social Science", "Science"],
|
||||
["English", "Computers", "Math", "Lunch", "Hindi", "Social Science", "Science"],
|
||||
],
|
||||
});
|
||||
|
||||
export let colseModal = $state({ v: false });
|
31
src/routes/IdleScreen/logic/updateTime.js
Normal file
31
src/routes/IdleScreen/logic/updateTime.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
export function updateTime(RowsObject, Digit, type) {
|
||||
let DigitsHeight = parseInt(type == "sec" ? "75" : "200");
|
||||
let LastZeroPos = Digit == 1 ? 10 : Digit == 0 && type == "hour" ? 2 : 6;
|
||||
let currentTime;
|
||||
|
||||
if (type == "hour") {
|
||||
if (new Date().getHours() > 12) {
|
||||
currentTime = parseInt((new Date().getHours() - 12).toString().padStart(2, "0")[Digit]);
|
||||
} else {
|
||||
currentTime = parseInt(new Date().getHours().toString().padStart(2, "0")[Digit]);
|
||||
}
|
||||
} else if (type == "min") {
|
||||
currentTime = parseInt(new Date().getMinutes().toString().padStart(2, "0")[Digit]);
|
||||
} else if (type == "sec") {
|
||||
currentTime = parseInt(new Date().getSeconds().toString().padStart(2, "0")[Digit]);
|
||||
}
|
||||
|
||||
if (currentTime == 0) {
|
||||
if (RowsObject.scrollTop != 0) {
|
||||
RowsObject.scrollTop = LastZeroPos * DigitsHeight;
|
||||
setTimeout(() => {
|
||||
RowsObject.scroll({
|
||||
top: 0,
|
||||
behavior: "instant",
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
RowsObject.scrollTop = currentTime * DigitsHeight;
|
||||
}
|
||||
}
|
20
src/routes/announcer/+page.svelte
Normal file
20
src/routes/announcer/+page.svelte
Normal file
|
@ -0,0 +1,20 @@
|
|||
<script>
|
||||
import List from "./components/CommonAnounceedTexts/list.svelte";
|
||||
import Add from "./components/CustomText/add.svelte";
|
||||
import CustomText from "./components/CustomText/CustomText.svelte";
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import { LoadMostUsedAnnouncement } from "./logic/LoadMostUsedAnnouncement.js";
|
||||
onMount(() => LoadMostUsedAnnouncement());
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-col items-center justify-center gap-5 p-5">
|
||||
<div class="w-fit rounded-2xl bg-gray-900 p-3">
|
||||
<h1 class="text-center text-4xl">Most Announced announcements</h1>
|
||||
<List />
|
||||
<hr class="my-5 w-full border-gray-600" />
|
||||
<h1 class="text-center text-4xl">Or announce something else</h1>
|
||||
<CustomText />
|
||||
<Add />
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
<script>
|
||||
import { DeleteMostUsedAnnouncement } from "../../logic/AddAndDeleteMostUsedAnnouncements.js";
|
||||
|
||||
let props = $props();
|
||||
let announcementID = props.announcementID;
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="cursor-pointer rounded-2xl bg-gray-800 p-2.5 text-2xl transition-all hover:scale-120 hover:-rotate-15 hover:bg-gray-600"
|
||||
aria-label="Delete announcement"
|
||||
onclick={() => DeleteMostUsedAnnouncement(announcementID)}
|
||||
>
|
||||
<i class="nf nf-md-trash_can_outline"></i></button
|
||||
>
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import { MostUsedAnnouncements } from "../../logic/announcerData.svelte.js";
|
||||
import Delete from "./delete.svelte";
|
||||
import Text from "./text.svelte";
|
||||
</script>
|
||||
|
||||
{#each MostUsedAnnouncements.v as announcementText, announcementID}
|
||||
<div class="flex w-full gap-2 mt-2">
|
||||
<Text {announcementText} />
|
||||
<Delete {announcementID} />
|
||||
</div>
|
||||
{/each}
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
import { AnnounceUsingTTS } from "../../logic/AnnounceUsingTTS.js";
|
||||
|
||||
let props = $props();
|
||||
let announcementText = props.announcementText;
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="w-full cursor-pointer rounded-2xl bg-gray-800 p-2.5 text-2xl transition-all hover:scale-105 hover:bg-gray-600"
|
||||
onclick={() => AnnounceUsingTTS(announcementText)}
|
||||
>
|
||||
{announcementText}
|
||||
</button>
|
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
import Input from "./input.svelte";
|
||||
import Play from "./play.svelte";
|
||||
</script>
|
||||
|
||||
<div class="mt-2 flex gap-2">
|
||||
<Input />
|
||||
<Play />
|
||||
</div>
|
16
src/routes/announcer/components/CustomText/add.svelte
Normal file
16
src/routes/announcer/components/CustomText/add.svelte
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
import { AddMostUsedAnnouncement } from "../../logic/AddAndDeleteMostUsedAnnouncements.js";
|
||||
import { CurrentText } from "../../logic/announcerData.svelte.js";
|
||||
</script>
|
||||
|
||||
{#if CurrentText.v}
|
||||
<div class="flex w-full justify-center mt-2">
|
||||
<button
|
||||
class="text-1xl w-fit cursor-pointer self-center rounded-2xl bg-gray-800 p-2.5 transition-all hover:scale-110 hover:bg-gray-600"
|
||||
onclick={() => {
|
||||
AddMostUsedAnnouncement(CurrentText.v);
|
||||
CurrentText.v = "";
|
||||
}}>Add "{CurrentText.v}" to "Most Announced announcements"</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
9
src/routes/announcer/components/CustomText/input.svelte
Normal file
9
src/routes/announcer/components/CustomText/input.svelte
Normal file
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
import { CurrentText } from "../../logic/announcerData.svelte.js";
|
||||
</script>
|
||||
|
||||
<input
|
||||
bind:value={CurrentText.v}
|
||||
placeholder="Type in here what you want to announce"
|
||||
class="flex-1 rounded-2xl bg-gray-800 p-2.5 text-2xl"
|
||||
/>
|
11
src/routes/announcer/components/CustomText/play.svelte
Normal file
11
src/routes/announcer/components/CustomText/play.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script>
|
||||
import { AnnounceUsingTTS } from "../../logic/AnnounceUsingTTS.js";
|
||||
import { CurrentText } from "../../logic/announcerData.svelte.js";
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="cursor-pointer rounded-2xl bg-gray-800 p-2.5 text-2xl transition-all hover:scale-120 hover:-rotate-15 hover:bg-gray-600"
|
||||
onclick={() => AnnounceUsingTTS(CurrentText.v)}
|
||||
>
|
||||
📢</button
|
||||
>
|
|
@ -0,0 +1,13 @@
|
|||
import { MostUsedAnnouncements } from "./announcerData.svelte.js";
|
||||
|
||||
export function AddMostUsedAnnouncement(announcementText) {
|
||||
MostUsedAnnouncements.v.push(announcementText);
|
||||
localStorage.setItem("MostUsedAnnouncements", JSON.stringify(MostUsedAnnouncements.v));
|
||||
}
|
||||
|
||||
export function DeleteMostUsedAnnouncement(announcementID) {
|
||||
if (confirm("Are you sure you want to delete this announcement?")) {
|
||||
MostUsedAnnouncements.v.splice(announcementID, 1);
|
||||
localStorage.setItem("MostUsedAnnouncements", JSON.stringify(MostUsedAnnouncements.v));
|
||||
}
|
||||
}
|
7
src/routes/announcer/logic/AnnounceUsingTTS.js
Normal file
7
src/routes/announcer/logic/AnnounceUsingTTS.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export function AnnounceUsingTTS(text) {
|
||||
window.speechSynthesis.speak(
|
||||
Object.assign(new SpeechSynthesisUtterance(text), {
|
||||
rate: 0.5,
|
||||
}),
|
||||
);
|
||||
}
|
12
src/routes/announcer/logic/LoadMostUsedAnnouncement.js
Normal file
12
src/routes/announcer/logic/LoadMostUsedAnnouncement.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { MostUsedAnnouncements } from "./announcerData.svelte.js";
|
||||
|
||||
export function LoadMostUsedAnnouncement() {
|
||||
let TempMostUsedAnnouncements = JSON.parse(localStorage.getItem("MostUsedAnnouncements")) || "";
|
||||
|
||||
if (TempMostUsedAnnouncements == "") {
|
||||
MostUsedAnnouncements.v = ["Please be quiet"];
|
||||
localStorage.setItem("MostUsedAnnouncements", JSON.stringify(MostUsedAnnouncements.v));
|
||||
} else {
|
||||
MostUsedAnnouncements.v = TempMostUsedAnnouncements;
|
||||
}
|
||||
}
|
2
src/routes/announcer/logic/announcerData.svelte.js
Normal file
2
src/routes/announcer/logic/announcerData.svelte.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export let CurrentText = $state({ v: "" });
|
||||
export let MostUsedAnnouncements = $state({ v: [] });
|
18
src/routes/kahootclone/+page.svelte
Normal file
18
src/routes/kahootclone/+page.svelte
Normal file
|
@ -0,0 +1,18 @@
|
|||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
|
||||
<h1 class="m-[0] text-6xl">DaKahootClone</h1>
|
||||
<p class="m-[0] mb-2 text-lg text-gray-400">The best ever kahoot clone.</p>
|
||||
<a href="./kahootclone/join">
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>Join a game</button
|
||||
></a
|
||||
>
|
||||
<a href="./kahootclone/create">
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>Create and Host a game</button
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
28
src/routes/kahootclone/create/+page.svelte
Normal file
28
src/routes/kahootclone/create/+page.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<script>
|
||||
import UseDemoQuestions from "./components/buttons/UseDemoQuestions.svelte";
|
||||
import NewQuestion from "./components/buttons/NewQuestion.svelte";
|
||||
import StartGame from "./components/buttons/StartGame.svelte";
|
||||
import Question from "./components/Questions/question.svelte";
|
||||
import { questions, Wait } from "./logic/GameCreateData.svelte.js";
|
||||
import WaitStartGame from "./components/buttons/WaitStartGame.svelte";
|
||||
import GenerateQuetionsUsingAi from "./components/buttons/GenerateQuetionsUsingAI.svelte";
|
||||
</script>
|
||||
|
||||
<div class="bg-grey-900 flex justify-center p-5">
|
||||
<div
|
||||
class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||
>
|
||||
<div class="flex gap-3"><UseDemoQuestions /> <GenerateQuetionsUsingAi /></div>
|
||||
{#each questions.v as question, index}
|
||||
<Question {index} />
|
||||
{/each}
|
||||
<div class="flex gap-3">
|
||||
<NewQuestion />
|
||||
{#if Wait.v == false}
|
||||
<StartGame />
|
||||
{:else}
|
||||
<WaitStartGame />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import { questions } from '../../logic/GameCreateData.svelte.js';
|
||||
|
||||
let props = $props();
|
||||
let questionsIndex = props.questionsIndex;
|
||||
let index = props.answersIndex;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
value={index}
|
||||
name={index.toString()}
|
||||
bind:group={questions.v[questionsIndex].correctAnswer}
|
||||
class="input"
|
||||
/>
|
||||
|
||||
<input
|
||||
placeholder="Option {index + 1}"
|
||||
bind:value={questions.v[questionsIndex].answers[index]}
|
||||
class="w-[500px] rounded-lg bg-gray-800 p-1 text-center text-white"
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,54 @@
|
|||
<script>
|
||||
import DeleteQuestion from "../buttons/DeleteQuestion.svelte";
|
||||
import GenerateOptionsUsingAI from "../buttons/GenerateOptionsUsingAI.svelte";
|
||||
import Answers from "./answers.svelte";
|
||||
import { questions } from "../../logic/GameCreateData.svelte.js";
|
||||
|
||||
let props = $props();
|
||||
let index = props.index;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="mb-3 flex flex-col items-center justify-center gap-1 rounded-2xl bg-gray-600 p-2">
|
||||
<div class="flex h-fit items-center gap-3">
|
||||
<h1 class="mt-2 mb-3 text-2xl">Q{index + 1}.</h1>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={questions.v[index].name}
|
||||
placeholder="Question {index + 1}"
|
||||
class="h-fit w-[500px] rounded-xl bg-gray-800 p-1 text-center text-2xl text-white"
|
||||
/>
|
||||
<select
|
||||
bind:value={questions.v[index].answers.length}
|
||||
onchange={(e) => {
|
||||
const newLength = questions.v[index].answers.length;
|
||||
const currentAnswers = questions.v[index].answers;
|
||||
|
||||
if (newLength > currentAnswers.length) {
|
||||
// Add more answers
|
||||
while (questions.v[index].answers.length < newLength) {
|
||||
questions.v[index].answers.push("");
|
||||
}
|
||||
} else if (newLength < currentAnswers.length) {
|
||||
// Remove excess answers
|
||||
questions.v[index].answers = currentAnswers.slice(0, newLength);
|
||||
}
|
||||
}}
|
||||
class="h-fit rounded-xl bg-gray-800 p-1 text-center text-white"
|
||||
>
|
||||
<option disabled selected>Options</option>
|
||||
{#each Array(7) as _, i}
|
||||
<option value={i + 2}>{i + 2}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<DeleteQuestion {index} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each questions.v[index].answers as _, answersIndex}
|
||||
<Answers questionsIndex={index} {answersIndex} />
|
||||
{/each}
|
||||
</div>
|
||||
<GenerateOptionsUsingAI {index} />
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
<script>
|
||||
import { DeleteQuestion } from '../../logic/GameCreateData.svelte.js';
|
||||
|
||||
let props = $props();
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={() => DeleteQuestion(props.index)}
|
||||
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-red-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
|
||||
/></svg
|
||||
>Delete question</button
|
||||
>
|
|
@ -0,0 +1,19 @@
|
|||
<script>
|
||||
import { GenerateOptionsUsingAI } from "../../logic/GenerateOptionsUsingAI.js";
|
||||
import { questions } from "../../logic/GameCreateData.svelte.js";
|
||||
|
||||
let props = $props();
|
||||
let index = props.index;
|
||||
</script>
|
||||
|
||||
{#if questions.v[index].answers.every((answer) => answer === "")}
|
||||
<button
|
||||
onclick={() => {
|
||||
GenerateOptionsUsingAI(index);
|
||||
}}
|
||||
class="mt-1 mb-1 flex h-fit cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-5"
|
||||
>
|
||||
<i class="nf nf-cod-sparkle"></i>
|
||||
Generate Options using AI
|
||||
</button>
|
||||
{/if}
|
|
@ -0,0 +1,14 @@
|
|||
<script>
|
||||
import { GenerateQuestionsUsingAI } from "../../logic/GenerateQuestionsUsingAI.js";
|
||||
import { questions } from "../../logic/GameCreateData.svelte.js";
|
||||
</script>
|
||||
|
||||
{#if questions.v.length === 0 || (questions.v.length === 1 && questions.v[0].name === "" && questions.v[0].answers.every((answer) => answer === "") && questions.v[0].correctAnswer === undefined)}
|
||||
<button
|
||||
onclick={GenerateQuestionsUsingAI}
|
||||
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-5"
|
||||
>
|
||||
<i class="nf nf-cod-sparkle"></i>
|
||||
Generate questions using AI
|
||||
</button>
|
||||
{/if}
|
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import { AddQuestion } from '../../logic/GameCreateData.svelte.js';
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={() => AddQuestion()}
|
||||
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
|
||||
>New question</button
|
||||
>
|
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
import { startGame } from '../../logic/StartGame.js';
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={startGame}
|
||||
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
|
||||
/></svg
|
||||
>Start Quiz</button
|
||||
>
|
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import { SetQuestionsToDemoQuestions } from '../../logic/GameCreateData.svelte.js';
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={() => SetQuestionsToDemoQuestions()}
|
||||
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
|
||||
>Use demo questions</button
|
||||
>
|
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
import { startGame } from "../../logic/StartGame.js";
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={startGame}
|
||||
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-gray-700 p-2 transition-all hover:scale-110"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
|
||||
/></svg
|
||||
>Wait for game to be created</button
|
||||
>
|
36
src/routes/kahootclone/create/logic/GameCreateData.svelte.js
Normal file
36
src/routes/kahootclone/create/logic/GameCreateData.svelte.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { DefaultQuestions } from "$lib/config.js";
|
||||
|
||||
export let Wait = $state({
|
||||
v: false,
|
||||
});
|
||||
export let questions = $state({
|
||||
v: [
|
||||
{
|
||||
name: "",
|
||||
answers: ["", "", "", ""],
|
||||
correctAnswer: undefined,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export function SetQuestionsToDemoQuestions() {
|
||||
questions.v = DefaultQuestions;
|
||||
}
|
||||
|
||||
export function AddQuestion() {
|
||||
questions.v.push({
|
||||
name: "",
|
||||
answers: ["", "", "", ""],
|
||||
correctAnswer: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export function DeleteQuestion(index) {
|
||||
if (questions.v.length > 1) {
|
||||
if (confirm("Are you sure you want to delete this question? You cant undo this.")) {
|
||||
questions.v.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
alert("You need at least one question.");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { questions } from "./GameCreateData.svelte.js";
|
||||
import { AiPrompts } from "$lib/config.js";
|
||||
|
||||
export function GenerateOptionsUsingAI(index) {
|
||||
fetch("https://ai.hackclub.com/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: AiPrompts.GenerateOptionsUsingAI.replace(
|
||||
"[question]",
|
||||
questions.v[index].name,
|
||||
),
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
let question = questions.v[index].name;
|
||||
questions.v[index] = JSON.parse(data.choices[0].message.content);
|
||||
questions.v[index].name = question;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert("Error:" + error);
|
||||
return;
|
||||
});
|
||||
|
||||
alert("added!");
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { questions } from "./GameCreateData.svelte.js";
|
||||
import { AiPrompts } from "$lib/config.js";
|
||||
|
||||
export function GenerateQuestionsUsingAI() {
|
||||
let topic = window.prompt(
|
||||
"What is the topic of the questions?\nand the number of questions in the topic?",
|
||||
);
|
||||
|
||||
if (!topic) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("https://ai.hackclub.com/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: AiPrompts.GenerateQuestionsUsingAI.replace("[topic]", topic),
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
questions.v = JSON.parse(data.choices[0].message.content);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert("Error:" + error);
|
||||
return;
|
||||
});
|
||||
|
||||
alert("added!");
|
||||
}
|
51
src/routes/kahootclone/create/logic/InsertGameInDB.js
Normal file
51
src/routes/kahootclone/create/logic/InsertGameInDB.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { supabase } from "$lib/supabase";
|
||||
|
||||
export async function createGame(questions, gamePin) {
|
||||
const { data: gameData, error: gameError } = await supabase.from("games").insert({
|
||||
creator: "anonymous",
|
||||
creationdate: new Date().toISOString(),
|
||||
status: "lobby",
|
||||
gamepin: gamePin,
|
||||
});
|
||||
|
||||
if (gameError) {
|
||||
alert("Failed to create game: " + gameError.message + "\n\nPlease try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare questions and answers for batch insertion
|
||||
const questionsData = questions.map((q, index) => ({
|
||||
gameid: gamePin,
|
||||
questionstext: q.name,
|
||||
correctanswer: q.correctAnswer,
|
||||
}));
|
||||
|
||||
const { data: questionsResult, error: questionsError } = await supabase
|
||||
.from("questions")
|
||||
.insert(questionsData)
|
||||
.select("id");
|
||||
|
||||
if (questionsError) {
|
||||
alert("Failed to insert questions: " + questionsError.message + "\n\nPlease try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
const answersData = [];
|
||||
questionsResult.forEach((question, index) => {
|
||||
questions[index].answers.forEach((answer, answerIndex) => {
|
||||
answersData.push({
|
||||
questionid: question.id,
|
||||
content: answer,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const { error: answersError } = await supabase.from("answers").insert(answersData);
|
||||
|
||||
if (answersError) {
|
||||
alert("Failed to insert answers: " + answersError.message + "\n\nPlease try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = `/host?gamepin=${gamePin}` ;
|
||||
}
|
16
src/routes/kahootclone/create/logic/StartGame.js
Normal file
16
src/routes/kahootclone/create/logic/StartGame.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { createGame } from "./InsertGameInDB.js";
|
||||
import { questions,Wait } from "./GameCreateData.svelte.js";
|
||||
|
||||
export async function startGame() {
|
||||
if (questions.v.some((q) => q.name === "")) return alert("Please fill all questions");
|
||||
if (questions.v.some((q) => q.answers.some((a) => a === ""))) return alert("Fill all options");
|
||||
if (questions.v.some((q) => q.correctAnswer === undefined))
|
||||
return alert("Select correct answers");
|
||||
|
||||
const gamePin = Math.floor(Math.random() * 1000000)
|
||||
.toString()
|
||||
.padStart(6, "0");
|
||||
|
||||
Wait.v = true;
|
||||
|
||||
await createGame(questions.v, gamePin);}
|
30
src/routes/kahootclone/host/+page.svelte
Normal file
30
src/routes/kahootclone/host/+page.svelte
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script>
|
||||
import PlayingDisplay from "./components/DuringGame/display.svelte";
|
||||
|
||||
import LobbyDisplay from "./components/lobby/display.svelte";
|
||||
import { Status } from "./logic/HostsData.svelte.js";
|
||||
import { AutoUpdatePlayersList } from "./logic/UpdatePlayersList.js";
|
||||
import { GetCurrentPlayers } from "./logic/GetCurrentPlayers.js";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let gamePin;
|
||||
|
||||
onMount(() => {
|
||||
gamePin = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
|
||||
|
||||
GetCurrentPlayers(gamePin);
|
||||
AutoUpdatePlayersList(gamePin);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div
|
||||
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||
>
|
||||
{#if Status.v == "lobby"}
|
||||
<LobbyDisplay {gamePin} />
|
||||
{:else if Status.v == "started"}
|
||||
<PlayingDisplay />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
import { PeopleAwnseredQ, Totalplayers } from "./../../logic/HostsData.svelte.js";
|
||||
</script>
|
||||
|
||||
<div class="mt-2 mb-3 flex w-full flex-col rounded-2xl border-2 border-green-400 p-2">
|
||||
<h3>{PeopleAwnseredQ.v} out of {Totalplayers.v} have answered the question</h3>
|
||||
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||
<div
|
||||
class="h-4 rounded-full bg-green-600 transition-all duration-500"
|
||||
style="width: {(PeopleAwnseredQ.v / Totalplayers.v) * 100}%;"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import { CurrentQuestionDetails } from "../../../logic/HostsData.svelte.js";
|
||||
|
||||
import { AnswersSymbolAndColorScheme } from "$lib/config.js";
|
||||
</script>
|
||||
|
||||
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
|
||||
{#each CurrentQuestionDetails.v.answers as answer, index}
|
||||
<div class="flex">
|
||||
<input type="radio" name="question" class="sr-only" />
|
||||
<label
|
||||
for="O{index}"
|
||||
style="
|
||||
--border-color: {AnswersSymbolAndColorScheme[index].Color};
|
||||
--bg-color: {AnswersSymbolAndColorScheme[index].Color};
|
||||
"
|
||||
class="w-full rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all"
|
||||
>
|
||||
<i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
|
||||
{answer}
|
||||
</label>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
import { currentQuestion, totalQuetions } from "./../../../logic/HostsData.svelte.js";
|
||||
</script>
|
||||
|
||||
<div class="mt-5 mb-2 flex w-full items-center justify-center gap-3">
|
||||
<h3>Question {currentQuestion.v + 1} of {totalQuetions.v}</h3>
|
||||
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||
<div
|
||||
class="h-4 rounded-full bg-green-600 transition-all duration-700"
|
||||
style="width: {(currentQuestion.v / totalQuetions.v) * 100}%;"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
import Question from "./text/Quetion.svelte";
|
||||
import Awnsers from "./Awnsers.svelte";
|
||||
import ProgressBar from "./ProgressBar.svelte";
|
||||
</script>
|
||||
|
||||
<ProgressBar />
|
||||
<Question />
|
||||
<Awnsers />
|
|
@ -0,0 +1,7 @@
|
|||
<script>
|
||||
import { currentQuestion, CurrentQuestionDetails } from "../../../../logic/HostsData.svelte.js";
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] text-center text-5xl">
|
||||
Q{currentQuestion.v + 1}. {CurrentQuestionDetails.v.question}
|
||||
</h1>
|
|
@ -0,0 +1,8 @@
|
|||
<script>
|
||||
import PeopleAwnsered from "./PeopleAwnsered.svelte";
|
||||
import Display from "./awnseringQuetions/display.svelte";
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] text-7xl">HOSTING</h1>
|
||||
<Display />
|
||||
<PeopleAwnsered />
|
|
@ -0,0 +1,6 @@
|
|||
<script>
|
||||
let props = $props();
|
||||
let playerName = props.playerName;
|
||||
</script>
|
||||
|
||||
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl">{playerName}</span>
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import { players } from "../../../logic/HostsData.svelte.js";
|
||||
import PlayerBadge from "./playerBadge.svelte";
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
|
||||
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.v.length})</h1>
|
||||
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||
{#each players.v as playerName}
|
||||
<PlayerBadge {playerName} />
|
||||
{/each}
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import { startGame } from "../../../logic/startGame.js";
|
||||
|
||||
let props = $props();
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="mt-5 cursor-pointer rounded-2xl bg-green-700 p-2 text-4xl transition-all hover:scale-110 hover:-rotate-10"
|
||||
onclick={() => {
|
||||
startGame(props.gamePin);
|
||||
}}>Start the game</button
|
||||
>
|
15
src/routes/kahootclone/host/components/lobby/display.svelte
Normal file
15
src/routes/kahootclone/host/components/lobby/display.svelte
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import StartGame from "./buttons/startGame.svelte";
|
||||
import Players from "./PlayersGUI/players.svelte";
|
||||
|
||||
let props = $props();
|
||||
let gamePin = props.gamePin;
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] text-9xl">HOSTING</h1>
|
||||
<h1 class="m-[0] text-7xl">Game Pin:</h1>
|
||||
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
|
||||
{gamePin}
|
||||
</h1>
|
||||
<Players />
|
||||
<StartGame {gamePin} />
|
7
src/routes/kahootclone/host/logic/GameOver.js
Normal file
7
src/routes/kahootclone/host/logic/GameOver.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
|
||||
export async function GameOver(GamePin) {
|
||||
await supabase.from("games").update({ status: `completed` }).eq("gamepin", GamePin);
|
||||
|
||||
window.location.replace("/results?gamepin=" + GamePin + "&playerID=host-null");
|
||||
}
|
18
src/routes/kahootclone/host/logic/GetCurrentPlayers.js
Normal file
18
src/routes/kahootclone/host/logic/GetCurrentPlayers.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { players } from "./HostsData.svelte.js";
|
||||
|
||||
export async function GetCurrentPlayers(gamePin) {
|
||||
const { data, error } = await supabase
|
||||
.from("players")
|
||||
.select("playername")
|
||||
.eq("gameid", Number(gamePin));
|
||||
|
||||
console.log("Current players data:", JSON.stringify(data));
|
||||
|
||||
if (error) {
|
||||
console.error("Error fetching players:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
players.v = data ? data.map(player => player.playername) : [];
|
||||
}
|
10
src/routes/kahootclone/host/logic/HostsData.svelte.js
Normal file
10
src/routes/kahootclone/host/logic/HostsData.svelte.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export let players = $state({ v: [] });
|
||||
export let Status = $state({ v: "lobby" });
|
||||
export let questions = { v: {} };
|
||||
|
||||
export let currentQuestion = $state({ v: 0 });
|
||||
export let totalQuetions = $state({ v: 3 });
|
||||
export let PeopleAwnseredQ = $state({ v: 0 });
|
||||
export let Totalplayers = $state({ v: 3 });
|
||||
|
||||
export let CurrentQuestionDetails = $state({ v: {} });
|
26
src/routes/kahootclone/host/logic/UpdatePlayersList.js
Normal file
26
src/routes/kahootclone/host/logic/UpdatePlayersList.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { players } from "./HostsData.svelte.js";
|
||||
|
||||
export let LobbyConnection;
|
||||
|
||||
function onNewPlayer(Newplayers) {
|
||||
players.v.push(Newplayers.playername);
|
||||
}
|
||||
|
||||
export async function AutoUpdatePlayersList(gamePin) {
|
||||
LobbyConnection = supabase
|
||||
.channel("players-realtime")
|
||||
.on(
|
||||
"postgres_changes",
|
||||
{
|
||||
event: "INSERT",
|
||||
schema: "public",
|
||||
table: "players",
|
||||
filter: `gameid=eq.${gamePin}`,
|
||||
},
|
||||
(payload) => {
|
||||
onNewPlayer(payload.new);
|
||||
},
|
||||
)
|
||||
.subscribe();
|
||||
}
|
51
src/routes/kahootclone/host/logic/WaitForAwnser.js
Normal file
51
src/routes/kahootclone/host/logic/WaitForAwnser.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { onNewPlayerAwnsered } from "./onNewPlayerAwnsered.js";
|
||||
import { currentQuestion, questions, CurrentQuestionDetails } from "./HostsData.svelte.js";
|
||||
|
||||
let WaitingForAwnserConection;
|
||||
|
||||
export async function WaitForAwnser(questionid, gamePin) {
|
||||
if (questionid != 0) {
|
||||
await supabase.removeChannel(WaitingForAwnserConection);
|
||||
}
|
||||
|
||||
await supabase
|
||||
.from("games")
|
||||
.update({ status: `question-${currentQuestion.v}` })
|
||||
.eq("gamepin", gamePin);
|
||||
|
||||
WaitingForAwnserConection = supabase
|
||||
.channel("answeredby-realtime")
|
||||
.on(
|
||||
"postgres_changes",
|
||||
{
|
||||
event: "INSERT",
|
||||
schema: "public",
|
||||
table: "answeredby",
|
||||
filter: `questionid=eq.${questions.v[questionid].id}`,
|
||||
},
|
||||
(payload) => {
|
||||
onNewPlayerAwnsered(gamePin);
|
||||
},
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
const { data: questionsData } = await supabase
|
||||
.from("questions")
|
||||
.select("id,questionstext,correctanswer")
|
||||
.eq("gameid", Number(gamePin))
|
||||
.order("id", { ascending: true });
|
||||
|
||||
const { data: answers } = await supabase
|
||||
.from("answers")
|
||||
.select("content")
|
||||
.eq("questionid", Number(questionsData[currentQuestion.v].id))
|
||||
.order("id", { ascending: true });
|
||||
|
||||
CurrentQuestionDetails.v = {
|
||||
question: questionsData[currentQuestion.v].questionstext,
|
||||
correctAnswer: questionsData[currentQuestion.v].correctanswer,
|
||||
answers: answers.map((answer) => answer.content),
|
||||
questionid: questionsData[currentQuestion.v].id,
|
||||
};
|
||||
}
|
23
src/routes/kahootclone/host/logic/onNewPlayerAwnsered.js
Normal file
23
src/routes/kahootclone/host/logic/onNewPlayerAwnsered.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
Totalplayers,
|
||||
PeopleAwnseredQ,
|
||||
currentQuestion,
|
||||
totalQuetions,
|
||||
} from "./HostsData.svelte.js";
|
||||
import { GameOver } from "./GameOver.js";
|
||||
import { WaitForAwnser } from "./WaitForAwnser.js";
|
||||
|
||||
export async function onNewPlayerAwnsered(GamePin) {
|
||||
PeopleAwnseredQ.v++;
|
||||
|
||||
if (PeopleAwnseredQ.v == Totalplayers.v) {
|
||||
currentQuestion.v++;
|
||||
if (currentQuestion.v == totalQuetions.v) {
|
||||
GameOver(GamePin);
|
||||
return;
|
||||
}
|
||||
PeopleAwnseredQ.v = 0;
|
||||
|
||||
WaitForAwnser(currentQuestion.v, GamePin);
|
||||
}
|
||||
}
|
41
src/routes/kahootclone/host/logic/startGame.js
Normal file
41
src/routes/kahootclone/host/logic/startGame.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { LobbyConnection } from "./UpdatePlayersList.js";
|
||||
import {
|
||||
questions,
|
||||
Status,
|
||||
Totalplayers,
|
||||
totalQuetions,
|
||||
players,
|
||||
} from "./HostsData.svelte.js";
|
||||
import { WaitForAwnser } from "./WaitForAwnser.js";
|
||||
|
||||
export async function startGame(gamePin) {
|
||||
if (players.v.length == 0) {
|
||||
alert("you need at least 1 person to start the game!");
|
||||
return;
|
||||
}
|
||||
|
||||
await supabase.removeChannel(LobbyConnection);
|
||||
|
||||
Status.v = "started";
|
||||
|
||||
const { data } = await supabase
|
||||
.from("questions")
|
||||
.select("*")
|
||||
.eq("gameid", Number(gamePin))
|
||||
.order("id", { ascending: true });
|
||||
|
||||
questions.v = data;
|
||||
|
||||
totalQuetions.v = data.length;
|
||||
|
||||
const { data: playersData } = await supabase
|
||||
.from("players")
|
||||
.select("id")
|
||||
.eq("gameid", Number(gamePin))
|
||||
.order("id", { ascending: true });
|
||||
|
||||
Totalplayers.v = playersData.length;
|
||||
|
||||
WaitForAwnser(0, gamePin);
|
||||
}
|
47
src/routes/kahootclone/join/+page.svelte
Normal file
47
src/routes/kahootclone/join/+page.svelte
Normal file
|
@ -0,0 +1,47 @@
|
|||
<script>
|
||||
import { joinGame } from "./logic/joinGame.js";
|
||||
import { Checking } from "./logic/JoinGameData.svelte.js";
|
||||
|
||||
let pin;
|
||||
let name;
|
||||
</script>
|
||||
|
||||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div
|
||||
class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-7 shadow-lg"
|
||||
>
|
||||
<h1 class="m-[0] mb-3 text-5xl">Join a game here</h1>
|
||||
|
||||
<input
|
||||
placeholder="Enter game pin"
|
||||
class="rounded-lg bg-gray-800 p-2 text-center text-white"
|
||||
bind:value={pin}
|
||||
/>
|
||||
|
||||
<input
|
||||
placeholder="Enter your name"
|
||||
bind:value={name}
|
||||
class="rounded-lg bg-gray-800 p-2 text-center text-white"
|
||||
/>
|
||||
|
||||
{#if Checking.v}
|
||||
<button
|
||||
class="mt-2 cursor-pointer rounded-full bg-gray-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>
|
||||
Checking if pin is valid...
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="mt-2 cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
on:click={() => {
|
||||
if (!pin || !name) {
|
||||
alert("Please fill in the game pin and your name.");
|
||||
} else {
|
||||
joinGame(pin, name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Join game
|
||||
</button>{/if}
|
||||
</div>
|
||||
</div>
|
19
src/routes/kahootclone/join/logic/InsertPlayerInDB.js
Normal file
19
src/routes/kahootclone/join/logic/InsertPlayerInDB.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { supabase } from "$lib/supabase";
|
||||
|
||||
export async function addPlayer(name, gamePin) {
|
||||
const { data, error } = await supabase
|
||||
.from("players")
|
||||
.insert({
|
||||
gameid: gamePin,
|
||||
score: 0,
|
||||
playername: name,
|
||||
})
|
||||
.select("id");
|
||||
|
||||
if (error) {
|
||||
alert("Failed to join game: " + error.message + "\n\nPlease try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
return data[0].id;
|
||||
}
|
3
src/routes/kahootclone/join/logic/JoinGameData.svelte.js
Normal file
3
src/routes/kahootclone/join/logic/JoinGameData.svelte.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export let Checking = $state({
|
||||
v: false,
|
||||
});
|
19
src/routes/kahootclone/join/logic/joinGame.js
Normal file
19
src/routes/kahootclone/join/logic/joinGame.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { addPlayer } from "./InsertPlayerInDB.js";
|
||||
import { validateGamePin } from "./validateGamePin.js";
|
||||
import { Checking } from "./JoinGameData.svelte.js";
|
||||
|
||||
export async function joinGame(pin, name) {
|
||||
Checking.v = true;
|
||||
|
||||
if (!(await validateGamePin(pin))) {
|
||||
alert("Invalid game pin. Please try again.");
|
||||
Checking.v = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let id = await addPlayer(name, pin);
|
||||
|
||||
Checking.v = false;
|
||||
|
||||
window.location.href = `./play?gamepin=${pin}&name=${name}&playerid=${id}`;
|
||||
}
|
11
src/routes/kahootclone/join/logic/validateGamePin.js
Normal file
11
src/routes/kahootclone/join/logic/validateGamePin.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { supabase } from '$lib/supabase';
|
||||
|
||||
export async function validateGamePin(pin) {
|
||||
const { data, error } = await supabase
|
||||
.from('games')
|
||||
.select('gamepin')
|
||||
.eq('gamepin', Number(pin))
|
||||
.maybeSingle();
|
||||
|
||||
return data !== null && !error;
|
||||
}
|
34
src/routes/kahootclone/play/+page.svelte
Normal file
34
src/routes/kahootclone/play/+page.svelte
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import AwnserQuetion from "./components/awnseringQuetions/display.svelte";
|
||||
import LobbyDisplay from "./components/lobby/display.svelte";
|
||||
import { Status } from "./logic/HostsData.svelte.js";
|
||||
import { AutoUpdatePlayersList } from "./logic/UpdatePlayersList.js";
|
||||
import { GetCurrentPlayers } from "./logic/GetCurrentPlayers.js";
|
||||
import { IntializeGameStart } from "./logic/IntializeGameStart.js";
|
||||
import { onMount } from "svelte";
|
||||
import { name,playerid } from "./logic/HostsData.svelte.js";
|
||||
|
||||
let gamePin;
|
||||
|
||||
onMount(() => {
|
||||
name.v = new URLSearchParams(new URL(window.location.href).search).get("name");
|
||||
playerid.v = new URLSearchParams(new URL(window.location.href).search).get("playerid");
|
||||
gamePin = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
|
||||
|
||||
GetCurrentPlayers(gamePin);
|
||||
AutoUpdatePlayersList(gamePin);
|
||||
IntializeGameStart(gamePin);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div
|
||||
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||
>
|
||||
{#if Status.v == "lobby"}
|
||||
<LobbyDisplay {gamePin} />
|
||||
{:else if Status.v == "started"}
|
||||
<AwnserQuetion />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,36 @@
|
|||
<script>
|
||||
import { questions, Selected } from "../../logic/HostsData.svelte.js";
|
||||
import { AnswersSymbolAndColorScheme } from "$lib/config.js";
|
||||
</script>
|
||||
|
||||
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
|
||||
{#each questions.v.answers as answer, index}
|
||||
<div class="flex">
|
||||
<input
|
||||
type="radio"
|
||||
id="O{index}"
|
||||
name="question"
|
||||
class="peer sr-only"
|
||||
value={index}
|
||||
bind:group={Selected.v}
|
||||
/>
|
||||
|
||||
<label
|
||||
for="O{index}"
|
||||
style="
|
||||
--border-color: {AnswersSymbolAndColorScheme[index].Color};
|
||||
--bg-color: {AnswersSymbolAndColorScheme[index].Color};
|
||||
--border-color-checked: {AnswersSymbolAndColorScheme[index].SelectedColor};
|
||||
--bg-color-checked: {AnswersSymbolAndColorScheme[index].SelectedColor};
|
||||
--border-color-hover: {AnswersSymbolAndColorScheme[index].HoverBorderColor};
|
||||
--border-color-checked: {AnswersSymbolAndColorScheme[index].SelectedBorderColor};
|
||||
--border-color-hover: {AnswersSymbolAndColorScheme[index].HoverBorderColor};
|
||||
"
|
||||
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
|
||||
>
|
||||
<i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
|
||||
{answer}
|
||||
</label>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
import { CurrentQuestion, TotalQuestions } from "../../logic/HostsData.svelte.js";
|
||||
</script>
|
||||
|
||||
<div class="mb-5 flex w-full items-center justify-center gap-3">
|
||||
<h3>Question {CurrentQuestion.v + 1} of {TotalQuestions.v}</h3>
|
||||
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||
<div
|
||||
class="h-4 rounded-full bg-green-600 transition-all duration-700"
|
||||
style="width: {(CurrentQuestion.v / TotalQuestions.v) * 100}%;"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<div class="group relative">
|
||||
<button
|
||||
class="mt-4 cursor-not-allowed gap-0 rounded-lg bg-gray-500 p-2 text-3xl text-gray-300 transition-all"
|
||||
disabled
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<div
|
||||
class="invisible absolute bottom-full left-1/2 mb-2 w-40 -translate-x-1/2 rounded-md bg-black px-3 py-1 text-center text-sm text-white opacity-0 transition-all group-hover:visible group-hover:opacity-100"
|
||||
>
|
||||
Select an option to submit
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
import { SubmitAnswer } from "../../../logic/SubmitAnswer.js";
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="mt-4 cursor-pointer gap-0 rounded-lg bg-green-700 p-2 text-3xl transition-all hover:scale-110 hover:-rotate-10"
|
||||
onclick={SubmitAnswer}
|
||||
>Submit
|
||||
</button>
|
|
@ -0,0 +1,28 @@
|
|||
<script>
|
||||
import Question from "./text/Quetion.svelte";
|
||||
import Awnsers from "./Awnsers.svelte";
|
||||
import ProgressBar from "./ProgressBar.svelte";
|
||||
import SelectFirst from "./buttons/SelectFirst.svelte";
|
||||
import Wait from "./text/wait.svelte";
|
||||
import SubmitAwnser from "./buttons/submitAwnser.svelte";
|
||||
import { CurrentQuestion, Selected } from "../../logic/HostsData.svelte.js";
|
||||
</script>
|
||||
|
||||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div
|
||||
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||
>
|
||||
<ProgressBar />
|
||||
{#if CurrentQuestion.v != null}
|
||||
<Question />
|
||||
<Awnsers />
|
||||
{#if Selected.v != null}
|
||||
<SubmitAwnser />
|
||||
{:else}
|
||||
<SelectFirst />
|
||||
{/if}
|
||||
{:else}
|
||||
<Wait />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
<script>
|
||||
import { CurrentQuestion, questions } from "../../../logic/HostsData.svelte.js";
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] text-center text-5xl">
|
||||
Q{CurrentQuestion.v + 1}. {questions.v.question}
|
||||
</h1>
|
|
@ -0,0 +1 @@
|
|||
<h1 class="m-[0] text-center text-5xl">Please wait for everyone else to answer the question.</h1>
|
|
@ -0,0 +1,6 @@
|
|||
<script>
|
||||
let props = $props();
|
||||
let playerName = props.playerName;
|
||||
</script>
|
||||
|
||||
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl">{playerName}</span>
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import { players } from "../../../logic/HostsData.svelte.js";
|
||||
import PlayerBadge from "./playerBadge.svelte";
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] mt-5 text-6xl">Players Joined:</h1>
|
||||
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.v.length})</h1>
|
||||
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||
{#each players.v as playerName}
|
||||
<PlayerBadge {playerName} />
|
||||
{/each}
|
||||
</div>
|
18
src/routes/kahootclone/play/components/lobby/display.svelte
Normal file
18
src/routes/kahootclone/play/components/lobby/display.svelte
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
import Players from "./PlayersGUI/players.svelte";
|
||||
|
||||
let props = $props();
|
||||
let gamePin = props.gamePin;
|
||||
</script>
|
||||
|
||||
<h1 class="m-[0] text-9xl">PLAYING</h1>
|
||||
|
||||
<h1 class="m-[0] text-7xl text-center">Waiting for host to start the game</h1>
|
||||
|
||||
<!--
|
||||
<h1 class="m-[0] text-7xl">Game Pin:</h1>
|
||||
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
|
||||
{gamePin}
|
||||
</h1>
|
||||
-->
|
||||
<Players />
|
18
src/routes/kahootclone/play/logic/GetCurrentPlayers.js
Normal file
18
src/routes/kahootclone/play/logic/GetCurrentPlayers.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { players } from "./HostsData.svelte.js";
|
||||
|
||||
export async function GetCurrentPlayers(gamePin) {
|
||||
const { data, error } = await supabase
|
||||
.from("players")
|
||||
.select("playername")
|
||||
.eq("gameid", Number(gamePin));
|
||||
|
||||
console.log("Current players data:", JSON.stringify(data));
|
||||
|
||||
if (error) {
|
||||
console.error("Error fetching players:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
players.v = data ? data.map(player => player.playername) : [];
|
||||
}
|
9
src/routes/kahootclone/play/logic/HostsData.svelte.js
Normal file
9
src/routes/kahootclone/play/logic/HostsData.svelte.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
export let players = $state({ v: {} });
|
||||
export let Status = $state({ v: "lobby" });
|
||||
export let questions = $state({ v: {} });
|
||||
export let CurrentQuestion = $state({ v: null });
|
||||
export let TotalQuestions = $state({ v: 0 });
|
||||
export let Selected = $state({ v: null });
|
||||
export let isWait = $state({ v: true });
|
||||
export let name = $state({ v: "" });
|
||||
export let playerid = $state({ v: null });
|
22
src/routes/kahootclone/play/logic/IntializeGameStart.js
Normal file
22
src/routes/kahootclone/play/logic/IntializeGameStart.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { NewStatus } from "./NewStatus.js";
|
||||
|
||||
export async function IntializeGameStart(gamepin) {
|
||||
supabase
|
||||
.channel(`game_status_${gamepin}`)
|
||||
.on(
|
||||
"postgres_changes",
|
||||
{
|
||||
event: "UPDATE",
|
||||
schema: "public",
|
||||
table: "games",
|
||||
filter: `gamepin=eq.${gamepin}`,
|
||||
},
|
||||
(payload) => {
|
||||
if (payload.new.status) {
|
||||
NewStatus(payload.new.status, gamepin);
|
||||
}
|
||||
},
|
||||
)
|
||||
.subscribe();
|
||||
}
|
44
src/routes/kahootclone/play/logic/NewStatus.js
Normal file
44
src/routes/kahootclone/play/logic/NewStatus.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import {
|
||||
CurrentQuestion,
|
||||
Status,
|
||||
questions,
|
||||
isWait,
|
||||
Selected,
|
||||
playerid,
|
||||
TotalQuestions,
|
||||
} from "./HostsData.svelte.js";
|
||||
import { supabase } from "$lib/supabase.js";
|
||||
|
||||
export async function NewStatus(NewStatus, gamePin) {
|
||||
if (NewStatus == "completed") {
|
||||
window.location.replace("/results?gamepin" + gamePin + "&playerID=" + playerid.v);
|
||||
return;
|
||||
}
|
||||
|
||||
Status.v = "started";
|
||||
CurrentQuestion.v = Number(NewStatus.replace("question-", ""));
|
||||
|
||||
const { data: questionsData } = await supabase
|
||||
.from("questions")
|
||||
.select("id,questionstext,correctanswer")
|
||||
.eq("gameid", Number(gamePin))
|
||||
.order("id", { ascending: true });
|
||||
|
||||
TotalQuestions.v = questionsData.length;
|
||||
|
||||
const { data: answers } = await supabase
|
||||
.from("answers")
|
||||
.select("content")
|
||||
.eq("questionid", Number(questionsData[CurrentQuestion.v].id))
|
||||
.order("id", { ascending: true });
|
||||
|
||||
questions.v = {
|
||||
question: questionsData[CurrentQuestion.v].questionstext,
|
||||
correctAnswer: questionsData[CurrentQuestion.v].correctanswer,
|
||||
answers: answers.map((answer) => answer.content),
|
||||
questionid: questionsData[CurrentQuestion.v].id,
|
||||
};
|
||||
|
||||
isWait.v = false;
|
||||
Selected.v = null;
|
||||
}
|
31
src/routes/kahootclone/play/logic/SubmitAnswer.js
Normal file
31
src/routes/kahootclone/play/logic/SubmitAnswer.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { CurrentQuestion, Selected, questions, playerid } from "./HostsData.svelte.js";
|
||||
import { supabase } from "$lib/supabase.js";
|
||||
|
||||
export async function SubmitAnswer() {
|
||||
CurrentQuestion.v = null;
|
||||
|
||||
if (Selected.v == questions.v.correctAnswer) {
|
||||
await supabase
|
||||
.from("answeredby")
|
||||
.insert([
|
||||
{ questionid: questions.v.questionid, nameofanswerer: playerid.v, correct: true },
|
||||
])
|
||||
.select();
|
||||
|
||||
let { data: score } = await supabase.from("players").select("score").eq("id", playerid.v);
|
||||
|
||||
await supabase
|
||||
.from("players")
|
||||
.update({ score: score[0].score + 1 })
|
||||
.eq("id", playerid.v)
|
||||
.select();
|
||||
|
||||
} else {
|
||||
await supabase
|
||||
.from("answeredby")
|
||||
.insert([
|
||||
{ questionid: questions.v.questionid, nameofanswerer: playerid.v, correct: false },
|
||||
])
|
||||
.select();
|
||||
}
|
||||
}
|
26
src/routes/kahootclone/play/logic/UpdatePlayersList.js
Normal file
26
src/routes/kahootclone/play/logic/UpdatePlayersList.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { players } from "./HostsData.svelte.js";
|
||||
|
||||
export let LobbyConnection;
|
||||
|
||||
function onNewPlayer(Newplayers) {
|
||||
players.v.push(Newplayers.playername);
|
||||
}
|
||||
|
||||
export async function AutoUpdatePlayersList(gamePin) {
|
||||
LobbyConnection = supabase
|
||||
.channel("players-realtime")
|
||||
.on(
|
||||
"postgres_changes",
|
||||
{
|
||||
event: "INSERT",
|
||||
schema: "public",
|
||||
table: "players",
|
||||
filter: `gameid=eq.${gamePin}`,
|
||||
},
|
||||
(payload) => {
|
||||
onNewPlayer(payload.new);
|
||||
},
|
||||
)
|
||||
.subscribe();
|
||||
}
|
23
src/routes/kahootclone/play/logic/startGame.js
Normal file
23
src/routes/kahootclone/play/logic/startGame.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { supabase } from "$lib/supabase.js";
|
||||
import { LobbyConnection } from "./UpdatePlayersList.js";
|
||||
import { questions, Status, CurrentQuestion, TotalQuestions } from "./HostsData.svelte.js";
|
||||
|
||||
export async function startGame(gamePin) {
|
||||
await supabase.removeChannel(LobbyConnection);
|
||||
|
||||
Status.v = "started";
|
||||
|
||||
const { data } = await supabase
|
||||
.from("questions")
|
||||
.select("*")
|
||||
.eq("gameid", Number(gamePin))
|
||||
.order("id", { ascending: true });
|
||||
TotalQuestions.v = data.length;
|
||||
|
||||
CurrentQuestion.v = 0;
|
||||
|
||||
await supabase
|
||||
.from("games")
|
||||
.update({ status: `question-${CurrentQuestion.v}` })
|
||||
.eq("gamepin", gamePin);
|
||||
}
|
82
src/routes/kahootclone/results/+page.svelte
Normal file
82
src/routes/kahootclone/results/+page.svelte
Normal file
|
@ -0,0 +1,82 @@
|
|||
<script>
|
||||
import { supabase } from "$lib/supabase";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let gamePin;
|
||||
|
||||
let playerID;
|
||||
let players = [];
|
||||
|
||||
onMount(async () => {
|
||||
playerID = new URLSearchParams(new URL(window.location.href).search).get("playerID");
|
||||
gamePin = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
|
||||
|
||||
|
||||
let { data: fetchedPlayers } = await supabase
|
||||
.from("players")
|
||||
.select("*")
|
||||
.eq("gameid", gamePin);
|
||||
|
||||
players = fetchedPlayers.sort((a, b) => b.score - a.score);
|
||||
|
||||
console.log(players);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div
|
||||
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||
>
|
||||
<h1 class="mb-3 text-7xl font-bold text-white">Leaderboard</h1>
|
||||
|
||||
{#each players as player, i}
|
||||
{#if player.id == playerID}
|
||||
<div class="flex w-full items-center justify-between rounded-lg bg-green-950 p-2">
|
||||
<div
|
||||
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-gray-500"
|
||||
>
|
||||
{i + 1}
|
||||
</div>
|
||||
|
||||
<div class="w-20 text-white">{player.playername}</div>
|
||||
|
||||
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||
<div
|
||||
class="flex h-6 items-center justify-center rounded-full bg-green-600 transition-all duration-700"
|
||||
style="width: {(player.score / players[0].score) * 100}%;"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-17 justify-end">{player.score} points</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex w-full items-center justify-between rounded-lg bg-gray-800 p-2">
|
||||
<div
|
||||
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-gray-500"
|
||||
>
|
||||
{i + 1}
|
||||
</div>
|
||||
|
||||
<div class="w-20 text-white">{player.playername}</div>
|
||||
|
||||
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||
<div
|
||||
class="flex h-6 items-center justify-center rounded-full bg-green-600 transition-all duration-700"
|
||||
style="width: {(player.score / players[0].score) * 100}%;"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-17 justify-end">{player.score} points</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<a href="/"
|
||||
><button
|
||||
class="mt-4 cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>
|
||||
Go back to the home page!
|
||||
</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue