mirror of
https://github.com/Lissy93/personal-security-checklist.git
synced 2024-12-27 00:09:34 -05:00
Finishes progress component, improves styling for homepage, adds theme support, adds ignore option for table, starts working on filters for table, and more
This commit is contained in:
parent
fd639567dd
commit
5d665df3d4
@ -35,6 +35,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@builder.io/qwik": "^1.1.4",
|
"@builder.io/qwik": "^1.1.4",
|
||||||
|
"chart.js": "^4.4.1",
|
||||||
"marked": "^12.0.0",
|
"marked": "^12.0.0",
|
||||||
"progressbar.js": "^1.1.1",
|
"progressbar.js": "^1.1.1",
|
||||||
"sharp": "^0.33.2"
|
"sharp": "^0.33.2"
|
||||||
|
@ -109,6 +109,11 @@ const getSvgPath = (icon: string) => {
|
|||||||
vb: "0 0 512 512",
|
vb: "0 0 512 512",
|
||||||
path: "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z",
|
path: "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z",
|
||||||
};
|
};
|
||||||
|
case 'filters':
|
||||||
|
return {
|
||||||
|
vb: "0 0 512 512",
|
||||||
|
path: "M3.9 54.9C10.5 40.9 24.5 32 40 32H472c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9V448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6V320.9L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z",
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return { vb: "", path: "" }; // Default path or a placeholder icon
|
return { vb: "", path: "" }; // Default path or a placeholder icon
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import Icon from "../core/icon";
|
|||||||
|
|
||||||
export default component$(() => {
|
export default component$(() => {
|
||||||
return (
|
return (
|
||||||
<div class="hero bg-base-200 bg-opacity-25 mb-16">
|
<div class="hero bg-front mb-16 shadow-sm">
|
||||||
<div class="hero-content text-center">
|
<div class="hero-content text-center">
|
||||||
<div class="max-w-2xl flex flex-col place-items-center">
|
<div class="max-w-2xl flex flex-col place-items-center">
|
||||||
<p>The Ultimate</p>
|
<p>The Ultimate</p>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { component$ } from "@builder.io/qwik";
|
import { component$ } from "@builder.io/qwik";
|
||||||
|
|
||||||
|
import Icon from "~/components/core/icon";
|
||||||
import type { Priority, Section } from '../../types/PSC';
|
import type { Priority, Section } from '../../types/PSC';
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
||||||
|
|
||||||
export default component$((props: { section: Section }) => {
|
export default component$((props: { section: Section }) => {
|
||||||
|
|
||||||
const STORAGE_KEY = 'PSC_PROGRESS';
|
const [completed, setCompleted] = useLocalStorage('PSC_PROGRESS', {});
|
||||||
const [value, setValue] = useLocalStorage(STORAGE_KEY, {});
|
const [ignored, setIgnored] = useLocalStorage('PSC_IGNORED', {});
|
||||||
|
|
||||||
const getBadgeClass = (priority: Priority, precedeClass: string = '') => {
|
const getBadgeClass = (priority: Priority, precedeClass: string = '') => {
|
||||||
switch (priority.toLocaleLowerCase()) {
|
switch (priority.toLocaleLowerCase()) {
|
||||||
@ -30,12 +31,62 @@ export default component$((props: { section: Section }) => {
|
|||||||
return marked.parse(text || '', { async: false }) as string || '';
|
return marked.parse(text || '', { async: false }) as string || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const isChecked = (point: string) => {
|
const isIgnored = (pointId: string) => {
|
||||||
const pointId = generateId(point);
|
return ignored.value[pointId] || false;
|
||||||
return value.value[pointId] || false;
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const isChecked = (pointId: string) => {
|
||||||
|
if (isIgnored(pointId)) return false;
|
||||||
|
return completed.value[pointId] || false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<div class="collapse rounded-none">
|
||||||
|
<input type="checkbox" />
|
||||||
|
<div class="collapse-title flex justify-end font-bold">
|
||||||
|
<button class="btn btn-sm hover:bg-primary"><Icon width={16} height={16} icon="filters"/>Filters</button>
|
||||||
|
</div>
|
||||||
|
<div class="collapse-content flex flex-wrap justify-between bg-base-100 rounded px-4 pt-1 !pb-1">
|
||||||
|
{/* Filter by completion */}
|
||||||
|
<div class="flex justify-end items-center gap-1">
|
||||||
|
<p class="font-bold text-sm">Show</p>
|
||||||
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
|
<span class="text-sm">All</span>
|
||||||
|
<input type="radio" name="show-all" class="radio radio-sm checked:radio-info" checked />
|
||||||
|
</label>
|
||||||
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
|
<span class="text-sm">Remaining</span>
|
||||||
|
<input type="radio" name="show-remaining" class="radio radio-sm checked:radio-error" />
|
||||||
|
</label>
|
||||||
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
|
<span class="text-sm">Completed</span>
|
||||||
|
<input type="radio" name="show-completed" class="radio radio-sm checked:radio-success" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{/* Filter by level */}
|
||||||
|
<div class="flex justify-end items-center gap-1">
|
||||||
|
<p class="font-bold text-sm">Filter</p>
|
||||||
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
|
<span class="text-sm">Basic</span>
|
||||||
|
<input type="checkbox" checked class="checkbox checkbox-sm checked:checkbox-success" />
|
||||||
|
</label>
|
||||||
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
|
<span class="text-sm">Optional</span>
|
||||||
|
<input type="checkbox" checked class="checkbox checkbox-sm checked:checkbox-warning" />
|
||||||
|
</label>
|
||||||
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
|
<span class="text-sm">Advanced</span>
|
||||||
|
<input type="checkbox" checked class="checkbox checkbox-sm checked:checkbox-error" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -46,36 +97,66 @@ export default component$((props: { section: Section }) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{props.section?.checklist.map((item, index) => (
|
{props.section.checklist.map((item, index) => {
|
||||||
<tr key={index} class={`rounded-sm hover:bg-opacity-5 hover:bg-${getBadgeClass(item.priority)}`}>
|
const badgeColor = getBadgeClass(item.priority);
|
||||||
<td>
|
const itemId = generateId(item.point);
|
||||||
|
const isItemCompleted = isChecked(itemId);
|
||||||
|
const isItemIgnored = isIgnored(itemId);
|
||||||
|
return (
|
||||||
|
<tr key={index} class={[
|
||||||
|
'rounded-sm transition-all',
|
||||||
|
isItemCompleted ? `bg-${badgeColor} bg-opacity-10` : '',
|
||||||
|
isItemIgnored? 'bg-neutral bg-opacity-15' : '',
|
||||||
|
!isItemIgnored && !isItemCompleted ? `hover:bg-opacity-5 hover:bg-${badgeColor}` : '',
|
||||||
|
]}>
|
||||||
|
<td class="text-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="checkbox"
|
class={`checkbox checked:checkbox-${badgeColor} hover:checkbox-${badgeColor}`}
|
||||||
id={generateId(item.point)}
|
id={`done-${itemId}`}
|
||||||
checked={isChecked(item.point)}
|
checked={isChecked(itemId)}
|
||||||
|
disabled={isIgnored(itemId)}
|
||||||
onClick$={() => {
|
onClick$={() => {
|
||||||
const id = item.point.toLowerCase().replace(/ /g, '-');
|
const data = completed.value;
|
||||||
const data = value.value;
|
data[itemId] = !data[itemId];
|
||||||
data[id] = !data[id];
|
setCompleted(data);
|
||||||
setValue(data);
|
}}
|
||||||
|
/>
|
||||||
|
<label for={`ignore-${itemId}`} class="text-small block opacity-50 mt-2">Ignore</label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id={`ignore-${itemId}`}
|
||||||
|
class={`toggle toggle-xs toggle-${badgeColor}`}
|
||||||
|
checked={isIgnored(itemId)}
|
||||||
|
onClick$={() => {
|
||||||
|
const ignoredData = ignored.value;
|
||||||
|
ignoredData[itemId] = !ignoredData[itemId];
|
||||||
|
setIgnored(ignoredData);
|
||||||
|
|
||||||
|
const completedData = completed.value;
|
||||||
|
completedData[itemId] = false;
|
||||||
|
setCompleted(completedData);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<label class="text-base font-bold" for={generateId(item.point)}>
|
<label
|
||||||
|
for={`done-${itemId}`}
|
||||||
|
class={`text-base font-bold ${isIgnored(itemId) ? 'line-through' : 'cursor-pointer'}`}>
|
||||||
{item.point}
|
{item.point}
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class={`badge gap-2 ${getBadgeClass(item.priority, 'badge-')}`}>
|
<div class={`badge gap-2 badge-${badgeColor}`}>
|
||||||
{item.priority}
|
{item.priority}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td dangerouslySetInnerHTML={parseMarkdown(item.details)}></td>
|
<td dangerouslySetInnerHTML={parseMarkdown(item.details)}></td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
)}
|
||||||
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { $, component$, useSignal, useOnWindow, useContext } from "@builder.io/qwik";
|
import { $, component$, useSignal, useOnWindow, useContext } from "@builder.io/qwik";
|
||||||
|
import { Chart, registerables } from 'chart.js';
|
||||||
|
|
||||||
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
||||||
import { ChecklistContext } from "~/store/checklist-context";
|
import { ChecklistContext } from "~/store/checklist-context";
|
||||||
@ -18,6 +19,8 @@ export default component$(() => {
|
|||||||
const [checkedItems] = useLocalStorage('PSC_PROGRESS', {});
|
const [checkedItems] = useLocalStorage('PSC_PROGRESS', {});
|
||||||
// Store to hold calculated progress results
|
// Store to hold calculated progress results
|
||||||
const totalProgress = useSignal({ completed: 0, outOf: 0 });
|
const totalProgress = useSignal({ completed: 0, outOf: 0 });
|
||||||
|
// Ref to the radar chart canvas
|
||||||
|
const radarChart = useSignal<HTMLCanvasElement>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the users progress over specified sections.
|
* Calculates the users progress over specified sections.
|
||||||
@ -142,6 +145,111 @@ export default component$(() => {
|
|||||||
makeDataAndDrawChart('advanced', 'hsl(var(--er, 0 91% 71%))');
|
makeDataAndDrawChart('advanced', 'hsl(var(--er, 0 91% 71%))');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
interface RadarChartData {
|
||||||
|
labels: string[];
|
||||||
|
datasets: {
|
||||||
|
label: string;
|
||||||
|
data: number[];
|
||||||
|
[key: string]: any; // Anything else goes!
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the multi-dimensional data used for the radar chart
|
||||||
|
* based on each section, each level of priority, and the progress
|
||||||
|
* @param sections - The sections to build data from
|
||||||
|
*/
|
||||||
|
const makeRadarData = $((sections: Sections): Promise<RadarChartData> => {
|
||||||
|
// The labels for the corners of the chart, based on sections
|
||||||
|
const labels = sections.map((section: Section) => section.title);
|
||||||
|
// Items applied to every dataset
|
||||||
|
const datasetTemplate = {
|
||||||
|
borderWidth: 1,
|
||||||
|
};
|
||||||
|
// Helper function to asynchronously calculate percentage
|
||||||
|
const calculatePercentage = async (section: Section, priority: Priority) => {
|
||||||
|
const filteredSections = await filterByPriority([section], priority);
|
||||||
|
const progress = await calculateProgress(filteredSections);
|
||||||
|
return progress.outOf > 0 ? (progress.completed / progress.outOf) * 100 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Asynchronously build data for each priority level
|
||||||
|
const buildDataForPriority = (priority: Priority, color: string) => {
|
||||||
|
return Promise.all(sections.map(section => calculatePercentage(section, priority)))
|
||||||
|
.then(data => ({
|
||||||
|
...datasetTemplate,
|
||||||
|
label: priority.charAt(0).toUpperCase() + priority.slice(1),
|
||||||
|
data: data,
|
||||||
|
backgroundColor: color,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait on each set to resolve, and return the final data object
|
||||||
|
return Promise.all([
|
||||||
|
buildDataForPriority('recommended', 'hsl(158 64% 52%/75%)'),
|
||||||
|
buildDataForPriority('optional', 'hsl(43 96% 56%/75%)'),
|
||||||
|
buildDataForPriority('advanced', 'hsl(0 91% 71%/75%)'),
|
||||||
|
]).then(datasets => ({
|
||||||
|
labels,
|
||||||
|
datasets,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useOnWindow('load', $(() => {
|
||||||
|
Chart.register(...registerables);
|
||||||
|
|
||||||
|
makeRadarData(checklists.value).then((data) => {
|
||||||
|
if (radarChart.value) {
|
||||||
|
new Chart(radarChart.value, {
|
||||||
|
type: 'radar',
|
||||||
|
data,
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
r: {
|
||||||
|
angleLines: {
|
||||||
|
display: true,
|
||||||
|
color: '#7d7d7da1',
|
||||||
|
},
|
||||||
|
suggestedMin: 0,
|
||||||
|
suggestedMax: 100,
|
||||||
|
ticks: {
|
||||||
|
stepSize: 25,
|
||||||
|
callback: (value) => `${value}%`,
|
||||||
|
color: '#ffffffbf',
|
||||||
|
backdropColor: '#ffffff3b',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display: true,
|
||||||
|
color: '#7d7d7dd4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
labels: {
|
||||||
|
font: {
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: (ctx) => `Completed ${ctx.parsed.r}% of ${ctx.dataset.label || ''} items`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{ id: 'recommended-container', label: 'Essential' },
|
{ id: 'recommended-container', label: 'Essential' },
|
||||||
{ id: 'optional-container', label: 'Optional' },
|
{ id: 'optional-container', label: 'Optional' },
|
||||||
@ -150,9 +258,11 @@ export default component$(() => {
|
|||||||
|
|
||||||
// Beware, some god-awful markup ahead (thank Tailwind for that!)
|
// Beware, some god-awful markup ahead (thank Tailwind for that!)
|
||||||
return (
|
return (
|
||||||
<div class="flex justify-center flex-col w-full items-center">
|
<div class="flex justify-center flex-wrap items-stretch gap-6 mb-4">
|
||||||
<div class="mb-4">
|
|
||||||
<div class="rounded-box bg-neutral-content bg-opacity-5 w-96 p-4">
|
<div class="flex justify-center flex-col items-center gap-6">
|
||||||
|
{/* Progress Percent */}
|
||||||
|
<div class="rounded-box bg-front shadow-md w-96 p-4">
|
||||||
<h3 class="text-primary text-2xl">Your Progress</h3>
|
<h3 class="text-primary text-2xl">Your Progress</h3>
|
||||||
<p class="text-lg">
|
<p class="text-lg">
|
||||||
You've completed <b>{totalProgress.value.completed} out of {totalProgress.value.outOf}</b> items
|
You've completed <b>{totalProgress.value.completed} out of {totalProgress.value.outOf}</b> items
|
||||||
@ -163,18 +273,36 @@ export default component$(() => {
|
|||||||
max={totalProgress.value.outOf}>
|
max={totalProgress.value.outOf}>
|
||||||
</progress>
|
</progress>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="carousel rounded-box mb-8">
|
{/* Completion per level */}
|
||||||
|
<div class="carousel rounded-box">
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
class="flex flex-col justify-items-center carousel-item w-20 p-4
|
class="flex flex-col justify-items-center carousel-item w-20 p-4
|
||||||
bg-neutral-content bg-opacity-5 mx-2.5 rounded-box">
|
bg-front shadow-md mx-2.5 rounded-box">
|
||||||
<div class="relative" id={item.id}></div>
|
<div class="relative" id={item.id}></div>
|
||||||
<p class="text-center">{item.label}</p>
|
<p class="text-center">{item.label}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{/* Something ??? */}
|
||||||
|
<div class="p-4 rounded-box bg-front shadow-md w-96 flex-grow">
|
||||||
|
<p>Something else will go here....</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Radar Chart showing total progress per category and level */}
|
||||||
|
<div class="rounded-box bg-front shadow-md w-96 p-4">
|
||||||
|
<canvas ref={radarChart} id="myChart"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="justify-center flex-col items-center gap-6 hidden xl:flex">
|
||||||
|
{/* Remaining Tasks */}
|
||||||
|
<div class="p-4 rounded-box bg-front shadow-md w-96 flex-grow">
|
||||||
|
<p>Something else will go here....</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: grid;
|
/* I couldn't figure out how to do this with Tailwind.... */
|
||||||
gap: 2rem;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,15 @@ import styles from './psc.module.css';
|
|||||||
|
|
||||||
export default component$((props: { sections: Section[] }) => {
|
export default component$((props: { sections: Section[] }) => {
|
||||||
return (
|
return (
|
||||||
<div class={[styles.container, 'transition-all', 'px-4', 'mx-auto', 'max-w-6xl', 'w-full']}>
|
<div class={[styles.container, 'grid',
|
||||||
|
'mx-auto mt-8 px-4 gap-7', 'xl:px-10 xl:max-w-7xl',
|
||||||
|
'transition-all', 'max-w-6xl w-full']}>
|
||||||
{props.sections.map((section: Section) => (
|
{props.sections.map((section: Section) => (
|
||||||
<a key={section.slug}
|
<a key={section.slug}
|
||||||
href={`/checklist/${section.slug}`}
|
href={`/checklist/${section.slug}`}
|
||||||
class={['card card-side bg-base-200 bg-opacity-25 shadow-xl transition-all px-2',
|
class={['card card-side bg-front bg-opacity-25 shadow-md transition-all px-2',
|
||||||
`outline outline-10 outline-offset-2 outline-${section.color}-400`,
|
`outline-offset-2 outline-${section.color}-400`,
|
||||||
`hover:outline-offset-4 hover:bg-opacity-15 hover:bg-${section.color}-600`]}
|
`hover:outline hover:outline-10 hover:outline-offset-4 hover:bg-opacity-15 hover:bg-${section.color}-600`]}
|
||||||
>
|
>
|
||||||
<div class="flex-shrink-0 flex flex-col py-4 h-auto items-stretch justify-evenly">
|
<div class="flex-shrink-0 flex flex-col py-4 h-auto items-stretch justify-evenly">
|
||||||
<Icon icon={section.icon || 'star'} color={section.color} />
|
<Icon icon={section.icon || 'star'} color={section.color} />
|
||||||
|
@ -5,11 +5,11 @@ import type { DocumentHead } from "@builder.io/qwik-city";
|
|||||||
export default component$(() => {
|
export default component$(() => {
|
||||||
return (
|
return (
|
||||||
<div class="m-4 md:mx-16">
|
<div class="m-4 md:mx-16">
|
||||||
<article class="bg-base-200 bg-opacity-25 p-8 mx-auto max-w-[1200px] m-8 rounded-lg shadow-lg">
|
<article class="bg-back p-8 mx-auto max-w-[1200px] m-8 rounded-lg shadow-md">
|
||||||
<h2 class="text-2xl">About the Security Checklist</h2>
|
<h2 class="text-2xl">About the Security Checklist</h2>
|
||||||
</article>
|
</article>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<article class="bg-base-200 bg-opacity-25 p-8 mx-auto max-w-[1200px] m-8 rounded-lg shadow-lg">
|
<article class="bg-back p-8 mx-auto max-w-[1200px] m-8 rounded-lg shadow-md">
|
||||||
<h2 class="text-2xl">About the Author</h2>
|
<h2 class="text-2xl">About the Author</h2>
|
||||||
<p>
|
<p>
|
||||||
This project was originally started by me, Alicia Sykes- with a lot of help from the community.
|
This project was originally started by me, Alicia Sykes- with a lot of help from the community.
|
||||||
|
@ -22,7 +22,7 @@ export default component$(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="md:my-8 md:px-16 sm:px-2 rounded-md">
|
<div class="md:my-8 md:px-16 sm:px-2 rounded-md">
|
||||||
<article class="bg-base-200 bg-opacity-25 p-8 mx-auto w-full max-w-[1200px] rounded-lg shadow-lg">
|
<article class="bg-back p-8 mx-auto w-full max-w-[1200px] rounded-lg shadow-md">
|
||||||
<h1 class={['gap-2 text-5xl font-bold capitalize flex']}>
|
<h1 class={['gap-2 text-5xl font-bold capitalize flex']}>
|
||||||
<Icon height={36} width={36} icon={section?.icon || 'star'} />
|
<Icon height={36} width={36} icon={section?.icon || 'star'} />
|
||||||
{section?.title}
|
{section?.title}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
|
|
||||||
|
|
||||||
|
const applyCustomColors = (theme, front, back) => {
|
||||||
|
return {
|
||||||
|
...require("daisyui/src/theming/themes")[`[data-theme=${theme}]`],
|
||||||
|
"--front":front,
|
||||||
|
"--back": back || `${front} /0.75`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
||||||
plugins: [require('daisyui')],
|
plugins: [require('daisyui')],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
"front": "hsl(var(--front, 0deg 0% 60% / 10%))",
|
||||||
|
"back": "hsl(var(--back, 212 14% 10% / 1))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
daisyui: {
|
daisyui: {
|
||||||
themes: [
|
themes: [
|
||||||
"light",
|
{ light: applyCustomColors("light", "237 9% 86% / 0.75", "237 9% 86% / 1") },
|
||||||
"dark",
|
{ dark: applyCustomColors("dark", "217 14% 17%", "212 14% 10%") },
|
||||||
"cupcake",
|
{ night: applyCustomColors("night", "220deg 44.68% 9.22%", "219.2, 38.2%, 13.3%") },
|
||||||
"bumblebee",
|
|
||||||
"emerald",
|
{ cupcake: applyCustomColors("cupcake", "297deg 77% 90%", "303.33deg 60% 94.12%") },
|
||||||
"corporate",
|
{ bumblebee: applyCustomColors("bumblebee", "75.5deg 40% 87%", "60deg 23.08% 92.35%") },
|
||||||
"synthwave",
|
{ corporate: applyCustomColors("corporate", "211.67deg 43.9% 83.92%", "212.3, 25.5%, 90%") },
|
||||||
"retro",
|
{ synthwave: applyCustomColors("synthwave", "253.3, 58.1%, 12.2%", "253.5, 47.6%, 16.5%") },
|
||||||
"cyberpunk",
|
{ retro: applyCustomColors("retro", "41.9, 37.1%, 72%", "42.5, 36.4%, 87.1%") },
|
||||||
"valentine",
|
{ valentine: applyCustomColors("valentine", "320.4, 70.7%, 85.3%", "322.1, 61.3%, 93.9%") },
|
||||||
"halloween",
|
{ halloween: applyCustomColors("halloween", "0, 0%, 9%", "0, 0%, 16.9%") },
|
||||||
"garden",
|
{ aqua: applyCustomColors("aqua", "230.5, 41%, 27.3%", "230.8, 33.9%, 22.5%") },
|
||||||
"forest",
|
{ lofi: applyCustomColors("lofi", "228, 11.6%, 91.6%") },
|
||||||
"aqua",
|
{ fantasy: applyCustomColors("fantasy", "230.8, 33.9%, 22.5%", "210, 2.3%, 83.1%") },
|
||||||
"lofi",
|
{ dracula: applyCustomColors("dracula", "210, 2.3%, 83.1%, 0.03", "228, 20%, 14.7%") },
|
||||||
"pastel",
|
|
||||||
"fantasy",
|
|
||||||
"wireframe",
|
|
||||||
"black",
|
|
||||||
"luxury",
|
|
||||||
"dracula",
|
|
||||||
"cmyk",
|
|
||||||
"autumn",
|
|
||||||
"business",
|
|
||||||
"acid",
|
|
||||||
"lemonade",
|
|
||||||
"night",
|
|
||||||
"coffee",
|
|
||||||
"winter",
|
|
||||||
"dim",
|
|
||||||
"nord",
|
|
||||||
"sunset",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
safelist: [
|
safelist: [
|
||||||
@ -44,21 +44,8 @@ module.exports = {
|
|||||||
variants: ['light', 'dark', 'hover', 'focus'],
|
variants: ['light', 'dark', 'hover', 'focus'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: /(badge|bg)-(success|warning|error|info|neutral)/,
|
pattern: /(badge|bg|checkbox|toggle)-(success|warning|error|info|neutral)/,
|
||||||
variants: ['light', 'dark', 'hover', 'focus'],
|
variants: ['light', 'dark', 'hover', 'focus', 'checked'],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
'custom-blue': '#5b6d5b',
|
|
||||||
'custom-pink': {
|
|
||||||
100: '#ffecef',
|
|
||||||
400: '#ff69b4',
|
|
||||||
800: '#ff1493',
|
|
||||||
},
|
|
||||||
// Add more custom colors or extend existing ones here
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
@ -480,6 +480,11 @@
|
|||||||
"@jridgewell/resolve-uri" "^3.1.0"
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
|
"@kurkle/color@^0.3.0":
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
|
||||||
|
integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
|
||||||
|
|
||||||
"@mdx-js/mdx@2.3.0":
|
"@mdx-js/mdx@2.3.0":
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-2.3.0.tgz#d65d8c3c28f3f46bb0e7cb3bf7613b39980671a9"
|
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-2.3.0.tgz#d65d8c3c28f3f46bb0e7cb3bf7613b39980671a9"
|
||||||
@ -1019,6 +1024,13 @@ character-reference-invalid@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
|
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
|
||||||
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
|
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
|
||||||
|
|
||||||
|
chart.js@^4.4.1:
|
||||||
|
version "4.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.1.tgz#ac5dc0e69a7758909158a96fe80ce43b3bb96a9f"
|
||||||
|
integrity sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==
|
||||||
|
dependencies:
|
||||||
|
"@kurkle/color" "^0.3.0"
|
||||||
|
|
||||||
chokidar@^3.5.3:
|
chokidar@^3.5.3:
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
|
Loading…
Reference in New Issue
Block a user