Made dashboard, fixed navbar icons and padding, implemented search bar in mods and other various fixes
|
@ -16,6 +16,7 @@ import ModCreationPage from './pages/mod_creation'
|
||||||
|
|
||||||
import ModpacksPage from './pages/modpacks';
|
import ModpacksPage from './pages/modpacks';
|
||||||
|
|
||||||
|
import DashboardPage from './pages/dashboard';
|
||||||
import LoginPage from './pages/login';
|
import LoginPage from './pages/login';
|
||||||
import RegisterPage from './pages/register';
|
import RegisterPage from './pages/register';
|
||||||
import SettingsPage from './pages/settings';
|
import SettingsPage from './pages/settings';
|
||||||
|
@ -45,6 +46,8 @@ export function App() {
|
||||||
<AboutPage path="/about" />
|
<AboutPage path="/about" />
|
||||||
<SettingsPage path="/settings" />
|
<SettingsPage path="/settings" />
|
||||||
|
|
||||||
|
<DashboardPage path="/dashboard"/>
|
||||||
|
|
||||||
<ModPage path="/mods/:name"></ModPage>
|
<ModPage path="/mods/:name"></ModPage>
|
||||||
<ModCreationPage path="/create/mod" ></ModCreationPage>
|
<ModCreationPage path="/create/mod" ></ModCreationPage>
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M240.92-268.31q51-37.84 111.12-59.77Q412.15-350 480-350t127.96 21.92q60.12 21.93 111.12 59.77 37.3-41 59.11-94.92Q800-417.15 800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 62.85 21.81 116.77 21.81 53.92 59.11 94.92ZM480.01-450q-54.78 0-92.39-37.6Q350-525.21 350-579.99t37.6-92.39Q425.21-710 479.99-710t92.39 37.6Q610-634.79 610-580.01t-37.6 92.39Q534.79-450 480.01-450ZM480-100q-79.15 0-148.5-29.77t-120.65-81.08q-51.31-51.3-81.08-120.65Q100-400.85 100-480t29.77-148.5q29.77-69.35 81.08-120.65 51.3-51.31 120.65-81.08Q400.85-860 480-860t148.5 29.77q69.35 29.77 120.65 81.08 51.31 51.3 81.08 120.65Q860-559.15 860-480t-29.77 148.5q-29.77 69.35-81.08 120.65-51.3 51.31-120.65 81.08Q559.15-100 480-100Zm0-60q54.15 0 104.42-17.42 50.27-17.43 89.27-48.73-39-30.16-88.11-47Q536.46-290 480-290t-105.77 16.65q-49.31 16.66-87.92 47.2 39 31.3 89.27 48.73Q425.85-160 480-160Zm0-350q29.85 0 49.92-20.08Q550-550.15 550-580t-20.08-49.92Q509.85-650 480-650t-49.92 20.08Q410-609.85 410-580t20.08 49.92Q450.15-510 480-510Zm0-70Zm0 355Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="M243.95-254.31q54.9-37.97 111.72-59.73 56.82-21.75 124.33-21.75 67.51 0 124.41 21.75 56.9 21.76 111.8 59.73 42.02-45.05 65.98-101.91 23.96-56.87 23.96-123.78 0-136.38-94.88-231.27-94.89-94.88-231.27-94.88t-231.27 94.88Q153.85-616.38 153.85-480q0 66.91 24.03 123.78 24.04 56.86 66.07 101.91Zm235.89-208.77q-50.22 0-84.55-34.47-34.32-34.48-34.32-84.71t34.48-84.55q34.48-34.32 84.71-34.32 50.22 0 84.55 34.48 34.32 34.48 34.32 84.7 0 50.23-34.48 84.55t-84.71 34.32Zm.47 343.08q-75.46 0-141.01-28.04-65.54-28.04-114.38-76.99-48.84-48.94-76.88-114.22Q120-404.53 120-479.92q0-75.39 28.04-140.75t76.99-114.3q48.94-48.95 114.22-76.99Q404.53-840 479.92-840q75.39 0 140.75 28.04t114.3 76.99q48.95 48.94 76.99 114.32Q840-555.27 840-480.31q0 75.46-28.04 141.01-28.04 65.54-76.99 114.38-48.94 48.84-114.32 76.88Q555.27-120 480.31-120Zm-.31-33.85q56.13 0 111.28-19.42 55.16-19.42 98.05-56.78-42.89-33.41-96.07-52.66-53.18-19.24-113.26-19.24-60.08 0-113.64 18.86-53.57 18.86-95.18 53.04 42.38 37.36 97.54 56.78 55.15 19.42 111.28 19.42Zm.07-343.07q36.39 0 60.75-24.43t24.36-60.82q0-36.39-24.43-60.75t-60.82-24.36q-36.39 0-60.75 24.43t-24.36 60.82q0 36.39 24.43 60.75t60.82 24.36ZM480-582.1Zm0 354.2Z"/></svg>
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
1
frontend/src/assets/add.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="M463.08-463.08H240v-33.84h223.08V-720h33.84v223.08H720v33.84H496.92V-240h-33.84v-223.08Z"/></svg>
|
After Width: | Height: | Size: 213 B |
1
frontend/src/assets/dashboard.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="M536.92-590.77V-800H800v209.23H536.92ZM160-483.08V-800h263.08v316.92H160ZM536.92-160v-316.92H800V-160H536.92ZM160-160v-209.23h263.08V-160H160Zm33.85-356.92h195.38v-249.23H193.85v249.23Zm376.92 323.07h195.38v-249.23H570.77v249.23Zm0-430.77h195.38v-141.53H570.77v141.53ZM193.85-193.85h195.38v-141.53H193.85v141.53Zm195.38-323.07Zm181.54-107.7Zm0 181.54Zm-181.54 107.7Z"/></svg>
|
After Width: | Height: | Size: 491 B |
1
frontend/src/assets/dashboard_add.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="M140-820h283.08v283.08H140V-820Zm33.85 33.69v208.41-208.41ZM536.92-820H820v283.08H536.92V-820Zm40.98 33.69v208.41-208.41ZM140-423.08h283.08V-140H140v-283.08Zm33.85 40.16v209.07-209.07Zm487.94-40.16h33.85v124.36H820v33.85H695.64V-140h-33.85v-124.87H536.92v-33.85h124.87v-124.36Zm-91.02-363.07v215.38h215.38v-215.38H570.77Zm-396.92 0v215.38h215.38v-215.38H173.85Zm0 396.92v215.38h215.38v-215.38H173.85Z"/></svg>
|
After Width: | Height: | Size: 525 B |
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e3e3e3"><path d="M480.23-140v-45.39h282.08q4.61 0 8.46-3.84 3.84-3.85 3.84-8.46v-564.62q0-4.61-3.84-8.46-3.85-3.84-8.46-3.84H480.23V-820h282.08q23.53 0 40.61 17.08T820-762.31v564.62q0 23.53-17.08 40.61T762.31-140H480.23Zm-35.77-187.69-33-32.23 97.39-97.39H140v-45.38h367.62l-97.39-97.39 32.62-32.61 153.3 153.5-151.69 151.5Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="M479.38-160v-33.85h262.16q9.23 0 16.92-7.69 7.69-7.69 7.69-16.92v-523.08q0-9.23-7.69-16.92-7.69-7.69-16.92-7.69H479.38V-800h262.16q24.58 0 41.52 16.94Q800-766.12 800-741.54v523.08q0 24.58-16.94 41.52Q766.12-160 741.54-160H479.38Zm-20.51-186.41-24.69-23.9 92.77-92.77H160v-33.84h366.64l-92.77-92.77 24.18-24.41 134.26 134.51-133.44 133.18Z"/></svg>
|
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 463 B |
1
frontend/src/assets/logout.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="M218.46-160q-24.58 0-41.52-16.94Q160-193.88 160-218.46v-523.08q0-24.58 16.94-41.52Q193.88-800 218.46-800h262.16v33.85H218.46q-9.23 0-16.92 7.69-7.69 7.69-7.69 16.92v523.08q0 9.23 7.69 16.92 7.69 7.69 16.92 7.69h262.16V-160H218.46Zm448.1-186.41-24.69-23.9 92.77-92.77H367.69v-33.84h366.64l-92.77-92.77 24.18-24.41L800-479.59 666.56-346.41Z"/></svg>
|
After Width: | Height: | Size: 463 B |
1
frontend/src/assets/search.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M781.69-136.92 530.46-388.16q-30 24.77-69 38.77-39 14-80.69 14-102.55 0-173.58-71.01-71.03-71.01-71.03-173.54 0-102.52 71.01-173.6 71.01-71.07 173.54-71.07 102.52 0 173.6 71.03 71.07 71.03 71.07 173.58 0 42.85-14.38 81.85-14.39 39-38.39 67.84l251.23 251.23-42.15 42.16ZM380.77-395.38q77.31 0 130.96-53.66 53.66-53.65 53.66-130.96t-53.66-130.96q-53.65-53.66-130.96-53.66t-130.96 53.66Q196.15-657.31 196.15-580t53.66 130.96q53.65 53.66 130.96 53.66Z"/></svg>
|
After Width: | Height: | Size: 572 B |
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="m405.38-120-14.46-115.69q-19.15-5.77-41.42-18.16-22.27-12.38-37.88-26.53L204.92-235l-74.61-130 92.23-69.54q-1.77-10.84-2.92-22.34-1.16-11.5-1.16-22.35 0-10.08 1.16-21.19 1.15-11.12 2.92-25.04L130.31-595l74.61-128.46 105.93 44.61q17.92-14.92 38.77-26.92 20.84-12 40.53-18.54L405.38-840h149.24l14.46 116.46q23 8.08 40.65 18.54 17.65 10.46 36.35 26.15l109-44.61L829.69-595l-95.31 71.85q3.31 12.38 3.7 22.73.38 10.34.38 20.42 0 9.31-.77 19.65-.77 10.35-3.54 25.04L827.92-365l-74.61 130-107.23-46.15q-18.7 15.69-37.62 26.92-18.92 11.23-39.38 17.77L554.62-120H405.38ZM440-160h78.23L533-268.31q30.23-8 54.42-21.96 24.2-13.96 49.27-38.27L736.46-286l39.77-68-87.54-65.77q5-17.08 6.62-31.42 1.61-14.35 1.61-28.81 0-15.23-1.61-28.81-1.62-13.57-6.62-29.88L777.77-606 738-674l-102.08 42.77q-18.15-19.92-47.73-37.35-29.57-17.42-55.96-23.11L520-800h-79.77l-12.46 107.54q-30.23 6.46-55.58 20.81-25.34 14.34-50.42 39.42L222-674l-39.77 68L269-541.23q-5 13.46-7 29.23t-2 32.77q0 15.23 2 30.23t6.23 29.23l-86 65.77L222-286l99-42q23.54 23.77 48.88 38.12 25.35 14.34 57.12 22.34L440-160Zm38.92-220q41.85 0 70.93-29.08 29.07-29.07 29.07-70.92t-29.07-70.92Q520.77-580 478.92-580q-42.07 0-71.04 29.08-28.96 29.07-28.96 70.92t28.96 70.92Q436.85-380 478.92-380ZM480-480Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e3e3e3"><path d="m410.72-120-15.59-114.87q-21.1-6.59-46.12-20.51-25.01-13.93-42.42-29.88l-106.03 47.03-69.43-122.41 93.51-69.69q-1.92-11.67-3.15-24.45t-1.23-24.45q0-10.9 1.23-23.55 1.23-12.66 3.15-26.89l-93.51-70.2 69.43-120.36 105.26 45.74q19.72-16.2 43.23-29.74 23.51-13.54 45.31-20.23L410.72-840h138.56l15.59 115.64q24.44 8.9 45.43 20.82 20.98 11.92 40.8 29.05l108.85-45.74 68.92 120.36-96.59 71.69q3.46 13.36 4.18 25.24.72 11.89.72 22.94 0 10.28-1.1 22.09-1.11 11.81-3.87 26.96l95.56 70.31-69.44 122.41-107.23-47.8q-20.48 17.49-42 30.59-21.51 13.11-44.23 19.8L549.28-120H410.72Zm28.15-33.85h81.16l14.76-110.25q30.54-8 55.71-22.45t51.06-39.07l101.88 43.83 39.92-69.13-89.8-66.9q4.34-18.05 6.29-32.81 1.95-14.75 1.95-29.37 0-15.9-1.88-29.88-1.87-13.99-6.36-30.76l91.34-68.44-39.93-69.13-104.17 43.9q-18.83-20.9-49.04-39.14-30.22-18.24-57.73-22.45l-12.9-110.25h-82.18l-12.46 109.48q-31.72 6.31-57.79 21.14-26.06 14.84-51.29 40.38l-102.38-43.06-39.93 69.13 89.54 66.05q-4.85 14.29-6.92 30.29-2.08 16-2.08 33.51 0 15.9 2.08 31.13 2.07 15.23 6.15 30.28l-88.77 66.9 39.93 69.13 101.61-43.13q23.85 24.43 50.17 39.01 26.32 14.58 58.91 22.58l13.15 109.48Zm39.23-229.23q40.72 0 68.82-28.1 28.11-28.1 28.11-68.82 0-40.72-28.11-68.82-28.1-28.1-68.82-28.1-40.28 0-68.6 28.1-28.32 28.1-28.32 68.82 0 40.72 28.32 68.82 28.32 28.1 68.6 28.1ZM480-480Z"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
1
frontend/src/assets/settings_small.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="m405.38-120-14.46-115.69q-19.15-5.77-41.42-18.16-22.27-12.38-37.88-26.53L204.92-235l-74.61-130 92.23-69.54q-1.77-10.84-2.92-22.34-1.16-11.5-1.16-22.35 0-10.08 1.16-21.19 1.15-11.12 2.92-25.04L130.31-595l74.61-128.46 105.93 44.61q17.92-14.92 38.77-26.92 20.84-12 40.53-18.54L405.38-840h149.24l14.46 116.46q23 8.08 40.65 18.54 17.65 10.46 36.35 26.15l109-44.61L829.69-595l-95.31 71.85q3.31 12.38 3.7 22.73.38 10.34.38 20.42 0 9.31-.77 19.65-.77 10.35-3.54 25.04L827.92-365l-74.61 130-107.23-46.15q-18.7 15.69-37.62 26.92-18.92 11.23-39.38 17.77L554.62-120H405.38ZM440-160h78.23L533-268.31q30.23-8 54.42-21.96 24.2-13.96 49.27-38.27L736.46-286l39.77-68-87.54-65.77q5-17.08 6.62-31.42 1.61-14.35 1.61-28.81 0-15.23-1.61-28.81-1.62-13.57-6.62-29.88L777.77-606 738-674l-102.08 42.77q-18.15-19.92-47.73-37.35-29.57-17.42-55.96-23.11L520-800h-79.77l-12.46 107.54q-30.23 6.46-55.58 20.81-25.34 14.34-50.42 39.42L222-674l-39.77 68L269-541.23q-5 13.46-7 29.23t-2 32.77q0 15.23 2 30.23t6.23 29.23l-86 65.77L222-286l99-42q23.54 23.77 48.88 38.12 25.35 14.34 57.12 22.34L440-160Zm38.92-220q41.85 0 70.93-29.08 29.07-29.07 29.07-70.92t-29.07-70.92Q520.77-580 478.92-580q-42.07 0-71.04 29.08-28.96 29.07-28.96 70.92t28.96 70.92Q436.85-380 478.92-380ZM480-480Z"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -48,3 +48,14 @@
|
||||||
background-color: #eaeaea16;
|
background-color: #eaeaea16;
|
||||||
border-color: #eaeaea;
|
border-color: #eaeaea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
background-color: #de3535;
|
||||||
|
border-color: #e85f5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete:hover {
|
||||||
|
border-color: #fb7777;
|
||||||
|
filter: drop-shadow(#de3535 0 0 .6em);
|
||||||
|
|
||||||
|
}
|
|
@ -1,52 +1,53 @@
|
||||||
|
// Preact
|
||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { useState } from 'preact/hooks';
|
import { useState, useRef, useEffect } from 'preact/hooks';
|
||||||
import { searchMods } from '../services/api'; // Your API fetching function
|
|
||||||
import styles from './SearchBar.module.css'; // Optional: CSS Modules
|
|
||||||
|
|
||||||
function SearchBar({ onResults }) {
|
// Images
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
import SearchIcon from '../../assets/search.svg'
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
|
|
||||||
const handleInputChange = (event) => {
|
// Styles
|
||||||
setSearchTerm(event.target.value);
|
import styles from './search.module.css'; // Optional: CSS Modules
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = async () => {
|
|
||||||
if (!searchTerm.trim()) {
|
|
||||||
onResults([]); // Clear results if search term is empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
function SearchBar({ onSearch }) {
|
||||||
setError(null);
|
|
||||||
|
|
||||||
try {
|
const [search_input, setSearchInput] = useState('');
|
||||||
const results = await searchItems(searchTerm);
|
const timeout_id = useRef(null);
|
||||||
onResults(results); // Pass the fetched results to the parent component
|
|
||||||
} catch (err) {
|
|
||||||
setError('Failed to fetch search results.');
|
|
||||||
onResults([]); // Clear results on error
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const handleInputChange = (event) => {
|
||||||
<div className={styles.searchBarContainer}>
|
|
||||||
<input
|
const new_search_input = event.target.value;
|
||||||
type="text"
|
setSearchTerm(new_search_input);
|
||||||
placeholder="Search items..."
|
|
||||||
value={searchTerm}
|
if (timeout_id.current) {
|
||||||
onChange={handleInputChange}
|
clearTimeout(timeout_id.current);
|
||||||
className={styles.searchInput}
|
}
|
||||||
/>
|
|
||||||
<button onClick={handleSearch} className={styles.searchButton} disabled={loading}>
|
timeout_id.current = setTimeout(() => {
|
||||||
{loading ? 'Searching...' : 'Search'}
|
onSearch(newSearchTerm);
|
||||||
</button>
|
}, 500);
|
||||||
{error && <div className={styles.errorMessage}>{error}</div>}
|
};
|
||||||
</div>
|
|
||||||
);
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (timeout_id.current) {
|
||||||
|
clearTimeout(timeout_id.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.searchBarContainer}>
|
||||||
|
<img src={SearchIcon} className={styles.searchIcon}></img>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
value={search_input}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className={styles.searchBarInput}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SearchBar;
|
export default SearchBar;
|
|
@ -0,0 +1,42 @@
|
||||||
|
.searchBarContainer {
|
||||||
|
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
/* padding: 0.5rem 1.2rem; */
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
background-color: #242424;
|
||||||
|
border: transparent;
|
||||||
|
border: .1rem solid #3a3a3a;
|
||||||
|
border-radius: 20rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBarInput {
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0.5em;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: 'IBM Plex Mono';
|
||||||
|
color: #ffffffc0;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
border: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBarInput:focus {
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchIcon {
|
||||||
|
margin-left: 1em;
|
||||||
|
width: 1.6em;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
width: 18rem;
|
width: 18rem;
|
||||||
min-height: 40rem;
|
min-height: 40rem;
|
||||||
|
padding: 1.3rem;
|
||||||
|
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
color: #eaeaea;
|
color: #eaeaea;
|
||||||
|
@ -16,4 +17,5 @@
|
||||||
border: #3a3a3a solid;
|
border: #3a3a3a solid;
|
||||||
border-width: .1em;
|
border-width: .1em;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
|
@ -10,8 +10,8 @@ import styles from './navbar.module.css'
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
import Settings from '../../assets/settings.svg'
|
import Settings from '../../assets/settings.svg'
|
||||||
import Login from '../../assets/login_small.svg'
|
import Login from '../../assets/login.svg'
|
||||||
import Account from '../../assets/account.svg'
|
import Dashboard from '../../assets/dashboard.svg'
|
||||||
|
|
||||||
function NavBar({ children, className, ...rest}) {
|
function NavBar({ children, className, ...rest}) {
|
||||||
|
|
||||||
|
@ -43,19 +43,19 @@ function NavBar({ children, className, ...rest}) {
|
||||||
|
|
||||||
<div className={styles.rightItems}>
|
<div className={styles.rightItems}>
|
||||||
|
|
||||||
{/* Display login button, or profile picture if connected */}
|
{/* Display login button, or dashboard if connected */}
|
||||||
{user ? (
|
{user ? (
|
||||||
<a className={styles.rightItem} href={'/users/' + user.username}>
|
<a className={styles.rightItem} href={'/dashboard'}>
|
||||||
<img src={Account} width={'170%'} className={styles.profile}></img>
|
<img src={Dashboard} className={styles.dashboard}></img>
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<a className={styles.rightItem} href='/login'>
|
<a className={styles.rightItem} href='/login'>
|
||||||
<img src={Login} width={'170%'} className={styles.login}></img>
|
<img src={Login} className={styles.login}></img>
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<a className={styles.rightItem} href='/settings'>
|
<a className={styles.rightItem} href='/settings'>
|
||||||
<img src={Settings} width={'170%'} className={styles.settings}></img>
|
<img src={Settings} className={styles.settings}></img>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-left: 2em;
|
padding: 1.3em;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leftItem {
|
.leftItem {
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
|
|
||||||
/* WIP */
|
/* WIP */
|
||||||
.rightItem {
|
.rightItem {
|
||||||
margin: 0 1em;
|
margin: 0 .5em;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
@ -54,28 +55,31 @@
|
||||||
|
|
||||||
.rightItems {
|
.rightItems {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 1em;
|
height: 100%;
|
||||||
margin-top: .3em;
|
/* margin-right: 1em; */
|
||||||
|
/* margin-top: .3em; */
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings {
|
.settings {
|
||||||
|
height: 100%;
|
||||||
border-radius: 100rem;
|
border-radius: 100rem;
|
||||||
transition: 600ms;
|
transition: 600ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile {
|
.dashboard {
|
||||||
border-radius: 100rem;
|
height: 100%;
|
||||||
|
border-radius: .5rem;
|
||||||
transition: 300ms;
|
transition: 300ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login {
|
.login {
|
||||||
|
height: 100%;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
transition: 600ms;
|
transition: 600ms;
|
||||||
margin-top: .06em;
|
margin-top: .06em;
|
||||||
height: 80%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login:hover {
|
.login:hover {
|
||||||
|
@ -87,6 +91,6 @@
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile:hover {
|
.dashboard:hover {
|
||||||
background-color: #eaeaea10;
|
background-color: #eaeaea10;
|
||||||
}
|
}
|
|
@ -26,7 +26,7 @@ function AboutPage() {
|
||||||
<div class='mainText'>
|
<div class='mainText'>
|
||||||
WF radio is a platform for all your personnal mods and modpacks.
|
WF radio is a platform for all your personnal mods and modpacks.
|
||||||
The difference with already existing big platforms is that radio is self-hosted and open-source.
|
The difference with already existing big platforms is that radio is self-hosted and open-source.
|
||||||
It's meant for your personnal works that you don't want to publish on the said platforms, but feel free to use it the way you want.
|
It's meant for your personnal works that you don't want to publish on the these platforms, but feel free to use it the way you want.
|
||||||
Don't hesitate to learn more about the project with the link below (not here yet).
|
Don't hesitate to learn more about the project with the link below (not here yet).
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1 +1,67 @@
|
||||||
// TODO
|
import { h } from 'preact';
|
||||||
|
import { useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Button from '../components/Buttons/button'
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import Logo from '../assets/logo.png'
|
||||||
|
import Add from '../assets/add.svg'
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
import styles from '../styles/dashboard.module.css'
|
||||||
|
|
||||||
|
function DashboardPage() {
|
||||||
|
|
||||||
|
const handleCreate = () => {
|
||||||
|
window.location.href('/create/mod');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<a href='/' target="_blank">
|
||||||
|
<img src={Logo} class="logoSmall img" alt="WF" />
|
||||||
|
<p class="logoSmall text"> dashboard </p>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class='container'>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<p className={styles.title}>
|
||||||
|
Favorites
|
||||||
|
</p>
|
||||||
|
<div className={styles.tiles}>
|
||||||
|
<div className={styles.emptyTile}>
|
||||||
|
<p className={styles.emptyTileText}>
|
||||||
|
You have no favorites for the moment
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<p className={styles.title}>
|
||||||
|
Your creations
|
||||||
|
</p>
|
||||||
|
<div className={styles.tiles}>
|
||||||
|
<a className={styles.emptyTile} href='/create/mod'>
|
||||||
|
<p className={styles.emptyTileText}>
|
||||||
|
<img src={Add} ></img>
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.toolbar}>
|
||||||
|
<div className={styles.toolbarRightItems}>
|
||||||
|
<Button
|
||||||
|
className={styles.createButton}
|
||||||
|
href='/create/mod'
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DashboardPage;
|
|
@ -21,7 +21,7 @@ function HomePage() {
|
||||||
<p class="logo text"> radio </p>
|
<p class="logo text"> radio </p>
|
||||||
</a>
|
</a>
|
||||||
<div class='title'>An open place for mods</div>
|
<div class='title'>An open place for mods</div>
|
||||||
<Button className='start-button' href='/mods'>Get started</Button>
|
<Button className='start-button' href='/mods'>Start exploring</Button>
|
||||||
<div class='background'></div>
|
<div class='background'></div>
|
||||||
<div class='halo'></div>
|
<div class='halo'></div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -15,7 +15,7 @@ import Button from '../components/Buttons/button';
|
||||||
import InputField from '../components/Fields/input_field';
|
import InputField from '../components/Fields/input_field';
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
import { login } from '../services/auth';
|
import { login } from '../services/users';
|
||||||
|
|
||||||
|
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
|
@ -129,7 +129,8 @@ function LoginPage() {
|
||||||
type='submit'
|
type='submit'
|
||||||
disabled={loginStatus === 'logging in'}
|
disabled={loginStatus === 'logging in'}
|
||||||
>
|
>
|
||||||
{loginStatus === 'logging in' ? 'Logging in' : 'Login'}
|
{loginStatus === 'logging in' ? 'Logging in' :
|
||||||
|
loginStatus === 'success' ? 'Success' : 'Login'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className={styles.loginButton}
|
className={styles.loginButton}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { listMods } from '../services/mods';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import FiltersPanel from '../components/Filters/panel'
|
import FiltersPanel from '../components/Filters/panel'
|
||||||
|
import SearchBar from '../components/Fields/search';
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
import logo from '../assets/logo.png'
|
import logo from '../assets/logo.png'
|
||||||
|
@ -18,18 +19,32 @@ import RowCard from '../components/Cards/row';
|
||||||
|
|
||||||
function ModsPage() {
|
function ModsPage() {
|
||||||
|
|
||||||
// UseState
|
// List mods
|
||||||
const [mods, setMods] = useState([]);
|
const [mods, setMods] = useState([]);
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
|
// Filters
|
||||||
|
const [search_input, setSearchInput] = useState('');
|
||||||
|
const [selected_categories, setSelectedCategories] = useState([]);
|
||||||
|
|
||||||
|
const handleSearch = (new_search_input) => {
|
||||||
|
setSearchInput(new_search_input);
|
||||||
|
};
|
||||||
|
|
||||||
// UseEffect
|
// UseEffect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
async function loadItems() {
|
async function loadItems() {
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(false);
|
setError(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetched_mods = await listMods();
|
const filters = {
|
||||||
|
search: search_input,
|
||||||
|
categories: selected_categories
|
||||||
|
};
|
||||||
|
const fetched_mods = await listMods(filters);
|
||||||
setMods(fetched_mods);
|
setMods(fetched_mods);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
|
@ -48,7 +63,10 @@ function ModsPage() {
|
||||||
<img src={logo} class="logo img" alt="WF" />
|
<img src={logo} class="logo img" alt="WF" />
|
||||||
<p class="logo text"> mods </p>
|
<p class="logo text"> mods </p>
|
||||||
</a>
|
</a>
|
||||||
<FiltersPanel></FiltersPanel>
|
<FiltersPanel>
|
||||||
|
<SearchBar onSearch={handleSearch} />
|
||||||
|
|
||||||
|
</FiltersPanel>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -91,7 +109,7 @@ function ModsPage() {
|
||||||
{mods.map((mod) => {
|
{mods.map((mod) => {
|
||||||
console.debug(mod.name);
|
console.debug(mod.name);
|
||||||
// return <div key={mod.name}>Test</div>
|
// return <div key={mod.name}>Test</div>
|
||||||
return <RowCard key={mod.name} item={mod}/>
|
return <RowCard key={mod.name} item={mod} />
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -16,7 +16,7 @@ import Button from '../components/Buttons/button';
|
||||||
import InputField from '../components/Fields/input_field';
|
import InputField from '../components/Fields/input_field';
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
import { register } from '../services/auth';
|
import { register } from '../services/users';
|
||||||
|
|
||||||
|
|
||||||
function RegisterPage() {
|
function RegisterPage() {
|
||||||
|
|
|
@ -4,6 +4,12 @@ import { useState, useEffect } from 'preact/hooks'; // If you need state for set
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
import { jwtDecode } from 'jwt-decode'
|
import { jwtDecode } from 'jwt-decode'
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
import { deleteUser } from '../services/users';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Button from '../components/Buttons/button';
|
||||||
|
|
||||||
// Styles
|
// Styles
|
||||||
import styles from '../styles/settings.module.css'
|
import styles from '../styles/settings.module.css'
|
||||||
|
|
||||||
|
@ -16,12 +22,17 @@ function SettingsPage() {
|
||||||
const [theme, setTheme] = useState('dark');
|
const [theme, setTheme] = useState('dark');
|
||||||
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
|
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
|
||||||
const [user, setUser] = useState(null)
|
const [user, setUser] = useState(null)
|
||||||
|
const [tab, setTab] = useState('global');
|
||||||
|
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
const token = Cookies.get('authToken');
|
const token = Cookies.get('authToken');
|
||||||
if (token) {
|
if (token) {
|
||||||
setUser(token);
|
const decoded_token = jwtDecode(token);
|
||||||
|
if (decoded_token && decoded_token.username) {
|
||||||
|
setUser(decoded_token.username);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[])
|
[])
|
||||||
|
@ -36,7 +47,18 @@ function SettingsPage() {
|
||||||
// Save notification preference
|
// Save notification preference
|
||||||
};
|
};
|
||||||
|
|
||||||
const logout = () => {
|
const handleUserDeletion = async () => {
|
||||||
|
try {
|
||||||
|
await deleteUser(user);
|
||||||
|
handleLogout();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Couldn't delete account: ", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
Cookies.remove('authToken');
|
Cookies.remove('authToken');
|
||||||
location.replace('/');
|
location.replace('/');
|
||||||
}
|
}
|
||||||
|
@ -48,15 +70,15 @@ function SettingsPage() {
|
||||||
<p className={styles.logoTitle}> settings </p>
|
<p className={styles.logoTitle}> settings </p>
|
||||||
</a>
|
</a>
|
||||||
<div className={styles.tabsContainer}>
|
<div className={styles.tabsContainer}>
|
||||||
<a href='/settings'>
|
<a onClick={() => {setTab('global')}}>
|
||||||
<p className={styles.tab}>Global</p>
|
<p className={styles.tab}>Global</p>
|
||||||
</a>
|
</a>
|
||||||
{user ? (
|
{user ? (
|
||||||
<>
|
<>
|
||||||
<a href='/notfound'>
|
<a onClick={() => {setTab('user')}}>
|
||||||
<p className={styles.tab}>User</p>
|
<p className={styles.tab}>User</p>
|
||||||
</a>
|
</a>
|
||||||
<a onClick={() => {logout()}}>
|
<a onClick={() => {handleLogout()}}>
|
||||||
<p className={`${styles.tab} ${styles.logout}`}>Logout</p>
|
<p className={`${styles.tab} ${styles.logout}`}>Logout</p>
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
|
@ -66,23 +88,46 @@ function SettingsPage() {
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
<div>
|
{ tab === 'global' ? (
|
||||||
<label htmlFor="theme">Theme:</label>
|
<>
|
||||||
<select id="theme" value={theme} onChange={handleThemeChange}>
|
<p>
|
||||||
<option value="light">Light</option>
|
<label htmlFor="theme">Theme:</label>
|
||||||
<option value="dark">Dark</option>
|
<select id="theme" value={theme} onChange={handleThemeChange}>
|
||||||
</select>
|
<option value="light">Light</option>
|
||||||
</div>
|
<option value="dark">Dark</option>
|
||||||
<div>
|
</select>
|
||||||
<label>
|
</p>
|
||||||
Enable Notifications:
|
<p>
|
||||||
<input
|
<label>
|
||||||
type="checkbox"
|
Enable Notifications:
|
||||||
checked={notificationsEnabled}
|
<input
|
||||||
onChange={handleNotificationsChange}
|
type="checkbox"
|
||||||
/>
|
checked={notificationsEnabled}
|
||||||
</label>
|
onChange={handleNotificationsChange}
|
||||||
</div>
|
/>
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) :
|
||||||
|
tab === 'user' ? (
|
||||||
|
<>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<p className={styles.title}>Danger zone</p>
|
||||||
|
<Button
|
||||||
|
variant='delete'
|
||||||
|
style='font-size: 1.2rem'
|
||||||
|
onClick={handleUserDeletion}
|
||||||
|
>
|
||||||
|
Delete account
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,7 +30,7 @@ export async function createMod(mod_data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function listMods() {
|
export async function listMods(filters) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/list/mods`);
|
const response = await fetch(`${API_BASE_URL}/list/mods`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|
|
@ -25,8 +25,6 @@ export async function login(username, password) {
|
||||||
|
|
||||||
export async function register(user_data) {
|
export async function register(user_data) {
|
||||||
try {
|
try {
|
||||||
console.debug(user_data);
|
|
||||||
console.debug(JSON.stringify(user_data));
|
|
||||||
const response = await fetch(`${API_BASE_URL}/users`, {
|
const response = await fetch(`${API_BASE_URL}/users`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -44,3 +42,22 @@ export async function register(user_data) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteUser(username) {
|
||||||
|
try {
|
||||||
|
const authToken = Cookies.get('authToken');
|
||||||
|
const response = await fetch(`${API_BASE_URL}/users/${username}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Authorization': authToken,
|
||||||
|
}});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
// console.error('Failed to fetch items:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ body {
|
||||||
|
|
||||||
* {
|
* {
|
||||||
transition: 200ms;
|
transition: 200ms;
|
||||||
|
-webkit-user-drag: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +32,12 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: #ffffff;
|
||||||
|
background: #353be5;
|
||||||
|
}
|
||||||
|
|
||||||
/* Logo */
|
/* Logo */
|
||||||
|
|
||||||
.logo.img {
|
.logo.img {
|
||||||
|
@ -87,6 +94,8 @@ a {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Titles */
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0rem;
|
left: 0rem;
|
||||||
|
@ -101,6 +110,7 @@ h1 {
|
||||||
font-size: 3em;
|
font-size: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Containers */
|
||||||
|
|
||||||
.content-container {
|
.content-container {
|
||||||
|
|
||||||
|
@ -137,6 +147,8 @@ h1 {
|
||||||
border-width: .1em;
|
border-width: .1em;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
|
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loadingContent {
|
.loadingContent {
|
||||||
|
|
71
frontend/src/styles/dashboard.module.css
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
.category {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
color: #eaeaea;
|
||||||
|
font-size: 1.6em;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiles {
|
||||||
|
|
||||||
|
height: 13rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
|
||||||
|
overflow-x: scroll;
|
||||||
|
overflow-y: visible;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
gap: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
height: 5rem;
|
||||||
|
padding: .5rem;
|
||||||
|
|
||||||
|
/* background-color: #9a9a9a; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbarRightItems {
|
||||||
|
position: absolute;
|
||||||
|
right: 1rem;
|
||||||
|
bottom: 1rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
.createButton {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyTile {
|
||||||
|
height: 99%;
|
||||||
|
width: 22em;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
background-color: #141414;
|
||||||
|
border: #3a3a3a .1rem solid;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyTileText {
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
color: #9a9a9a;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
|
@ -84,6 +84,6 @@
|
||||||
|
|
||||||
color: #eaeaead0;
|
color: #eaeaead0;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
text-align: justify;
|
text-align: center;
|
||||||
|
|
||||||
}
|
}
|
|
@ -59,6 +59,7 @@
|
||||||
|
|
||||||
.tab:hover {
|
.tab:hover {
|
||||||
background-color: #eaeaea20;
|
background-color: #eaeaea20;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab:active {
|
.tab:active {
|
||||||
|
@ -74,3 +75,14 @@
|
||||||
color: #eaeaea;
|
color: #eaeaea;
|
||||||
background-color: #bc3939;
|
background-color: #bc3939;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
background: none; /*Only to remove the empty warning for the moment */
|
||||||
|
}
|