Funcionnal custom checkboxes, fixed small card component and added the ability to launch both backend and frontend at once

This commit is contained in:
Gu://em_ 2025-05-25 13:16:10 +02:00
parent f8bf646565
commit ca96f57fb8
17 changed files with 1438 additions and 77 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules/

View file

@ -1,6 +1,6 @@
{
"name": "wf-radio-backend",
"version": "1.0.0",
"version": "Alpha 1",
"description": "",
"main": "server.js",
"scripts": {

View file

@ -1,7 +1,7 @@
{
"name": "wf-radio-frontend",
"private": true,
"version": "0.0.0",
"version": "Alpha 1",
"type": "module",
"scripts": {
"dev": "vite",

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M382-221.91 135.91-468l75.66-75.65L382-373.22l366.43-366.43L824.09-664 382-221.91Z"/></svg>

After

Width:  |  Height:  |  Size: 207 B

View file

@ -1,8 +1,10 @@
//TODO made by AI
import { h } from 'preact';
import { useState } from 'preact/hooks';
import styles from './checkbox.module.css';
// Assets
import Checkmark from '../../assets/checkmark.svg'
function Checkbox({ id, name, value, checked: initialChecked, onChange, label }) {
const [isChecked, setIsChecked] = useState(initialChecked || false);
@ -24,11 +26,8 @@ function Checkbox({ id, name, value, checked: initialChecked, onChange, label })
onChange={handleChange}
className={styles.nativeCheckbox} // Hide this element
/>
<label htmlFor={id} className={styles.checkbox}>
{/* This div will be our custom visual checkbox */}
<div className={styles.checkmark}>
{isChecked && <span>&#10003;</span>} {/* Checkmark icon */}
</div>
<label htmlFor={id} className={styles.customCheckbox}>
{isChecked && <img src={Checkmark} className={styles.checkmark} ></img>}
{label && <span className={styles.label}>{label}</span>}
</label>
</div>

View file

@ -1,51 +1,66 @@
/*TODO made by AI */
.checkboxContainer {
display: flex;
align-items: center;
cursor: pointer;
user-select: none; /* Prevent text selection on click */
margin: 1.5em .5em;
/* width: 100%; */
display: flex;
align-items: center;
user-select: none;
}
.nativeCheckbox {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
position: absolute;
height: 0;
width: 0;
user-select: none;
}
.customCheckbox {
position: relative;
display: inline-block;
width: 20px; /* Adjust size as needed */
height: 20px;
background-color: #eee;
border: 1px solid #ccc;
border-radius: 3px; /* Optional rounded corners */
position: relative;
height: 1.5em;
width: 1.5em;
min-height: 1em;
min-width: 1em;
width: 1.7em; /* Adjust size as needed */
height: 1.7em;
background-color: transparent;
border: #eaeaea .1rem solid;
border-radius: .2em;
display: inline-block;
box-sizing: border-box;
user-select: none;
transition: 200ms;
}
.customCheckbox:hover input ~ .checkmark {
background-color: #ddd;
.customCheckbox:hover {
background-color: #eaeaea20;
}
.nativeCheckbox:checked ~ .customCheckbox {
background-color: #2196F3; /* Active color */
border: 1px solid #2196F3;
background-color: #353be5;
border: #353be5 .1rem solid;
/* border-radius: .3em; */
}
.checkmark {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: white; /* Checkmark color */
font-size: 16px; /* Adjust checkmark size */
.nativeCheckbox:checked ~ .customCheckbox:hover {
border: #6b70f7 .1rem solid;
filter: drop-shadow(#353be580 0 0 .7em);
}
.label {
margin-left: 8px;
position: absolute;
top: 0;
margin-left: 2.5em;
}
.checkmark {
position: absolute;
transform: scale(1.2);
height: 100%;
}

View file

@ -1,6 +1,6 @@
.rowCard {
height: 7rem;
min-height: 7rem;
margin-bottom: 1rem;
background-color: #1a1a1a;

View file

@ -22,6 +22,8 @@ function SmallCard({item, variant, href}) {
const item_page = "/mods/" + item.name;
return (
<a className={styles.Card} href={href}>
<img src={Banner} className={styles.banner}></img>
<img src={Thumbnail} className={styles.thumbnail}></img>
<div className={styles.CardText}>
{item.display_name}
</div>

View file

@ -1,6 +1,9 @@
.emptyCard {
height: 100%;
width: 22em;
min-width: 22em;
min-height: 13rem;
align-items: center;
display: flex;
@ -25,27 +28,66 @@
}
.Card {
height: 100%;
width: 22em;
align-items: center;
display: flex;
background-color: #141414;
position: relative;
height: 100%;
min-width: 22em;
min-height: 13rem;
background-color: #101010;
border: #3a3a3a .1rem solid;
border-radius: 1rem;
box-sizing: border-box;
overflow: hidden;
}
.CardText {
padding: 1em;
width: 100%;
margin-left: 7rem;
margin-top: .2rem;
color: #9a9a9a;
text-align: center;
vertical-align: middle;
color: #eaeaea;
/* font-weight: 600; */
font-size: 1.3rem;
user-select: none;
}
.banner {
width: 100%;
height: 40%;
border-radius: 1rem 1rem 0 0;
filter: brightness(50%);
object-fit: cover;
}
.thumbnail {
position: absolute;
left: 1rem;
top: 25%;
width: 5rem;
height: 5rem;
padding: .5rem;
background-color: #101010;
border: #3a3a3a .1rem solid;
border-radius: 1rem;
box-sizing: border-box;
}
.Card:hover {
filter: drop-shadow(#353be515 0 0 1em);
transform: scale(1.01);
border-color: #6b70f7;
}

View file

@ -92,7 +92,7 @@ function DashboardPage() {
return (
<>
{base_page}
<div class='container'>
<div class={styles.container}>
<p>Loading</p>
</div>
</>
@ -103,7 +103,7 @@ function DashboardPage() {
return (
<>
{base_page}
<div class='container'>
<div class={styles.container}>
<p>Error: {creations_error}</p>
</div>
</>
@ -113,7 +113,7 @@ function DashboardPage() {
return (
<>
{base_page}
<div class='container'>
<div className={styles.container}>
<div className={styles.category}>
<p className={styles.title}>
Favorites
@ -149,6 +149,8 @@ function DashboardPage() {
/>
</div>
</div>
<div style="height: 2rem"/>
</div>
<div className={styles.toolbar}>
<div className={styles.toolbarRightItems}>
<Button
@ -159,7 +161,6 @@ function DashboardPage() {
</Button>
</div>
</div>
</div>
</>
);
}

View file

@ -9,12 +9,16 @@ import { listMods } from '../services/mods';
import FiltersPanel from '../components/Filters/panel'
import SearchBar from '../components/Fields/search';
// Assets
import '../styles/filters_bar.css'
// Images
import logo from '../assets/logo.png'
// Styles
// Components
import GridCard from '../components/Cards/grid';
import RowCard from '../components/Cards/row';
import Checkbox from '../components/Buttons/checkbox';
function ModsPage() {
@ -67,7 +71,19 @@ function ModsPage() {
</a>
<FiltersPanel>
<SearchBar onSearch={handleSearch} />
<div class='filterTitle'>
Platform
</div>
<div class='filterOptions'>
<Checkbox
id='client'
label='Client'
/>
<Checkbox
id='server'
label='Server'
/>
</div>
</FiltersPanel>
</>
);

View file

@ -9,6 +9,7 @@ import { deleteUser } from '../services/users';
// Components
import Button from '../components/Buttons/button';
import Checkbox from '../components/Buttons/checkbox';
// Styles
import styles from '../styles/settings.module.css'
@ -97,16 +98,12 @@ function SettingsPage() {
<option value="dark">Dark</option>
</select>
</p>
<p>
<label>
Enable Notifications:
<input
type="checkbox"
<Checkbox
id='notifications'
label='Enable Notifications'
checked={notificationsEnabled}
onChange={handleNotificationsChange}
/>
</label>
</p>
</>
) :
tab === 'user' ? (

View file

@ -129,6 +129,12 @@ h1 {
/* border: #3a3a3a solid; */
border-width: .1em;
border-radius: .5rem;
display: flex;
flex-direction: column;
gap: .5rem;
overflow: scroll;
}
.container {

View file

@ -1,3 +1,24 @@
.container {
position: absolute;
top: 11rem;
right: 4rem;
left: 22rem;
bottom: 4rem;
padding: 3rem;
padding-top: 2rem;
background-color: #1a1a1a;
color: #eaeaea;
border: #3a3a3a solid;
border-width: .1em;
border-radius: .5rem;
overflow: scroll;
}
.category {
margin-top: 0;
}
@ -14,8 +35,10 @@
.tiles {
height: 13rem;
margin-bottom: 3rem;
height: 14rem;
margin-bottom: 2rem;
padding: 1rem;
margin-right: -3rem;
overflow-x: scroll;
overflow-y: visible;
@ -26,20 +49,29 @@
.toolbar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
right: 5rem;
left: 23rem;
bottom: 5rem;
height: 5rem;
padding: .5rem;
height: 3rem;
min-width: 30rem;
padding: 1rem;
background-color: #141414;
border: #3a3a3a .1rem solid;
border-radius: .5rem;
display: flex;
justify-content: space-between;
align-items: center;
/* background-color: #9a9a9a; */
}
.toolbarRightItems {
position: absolute;
right: 1rem;
bottom: 1rem;
/* position: absolute; */
/* right: 1rem; */
/* bottom: 1rem; */
margin-left: auto;
}
.createButton {

View file

@ -0,0 +1,9 @@
.filterTitle {
font-weight: 600;
font-size:1.3em;
margin-top: 1.4em
}
.filterOptions {
font-size:0.9em;
}

20
package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "wf-radio",
"version": "1.0.0",
"description": "",
"scripts": {
"start:frontend": "cd frontend && pnpm dev",
"start:backend": "cd backend && pnpm start",
"build:frontend": "cd frontend && pnpm build",
"start:dev": "npm-run-all --parallel start:frontend start:backend",
"start:prod": "npm-run-all --parallel build:frontend start:backend",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.11.0",
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}

1220
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff