mirror of
https://github.com/Lissy93/personal-security-checklist.git
synced 2024-12-25 07:19:22 -05:00
project init
This commit is contained in:
parent
d5415b7747
commit
2fc27fa500
@ -1,13 +1,12 @@
|
|||||||
import { $, component$, useStore, useSignal } from "@builder.io/qwik";
|
import { $, component$, useStore, useSignal } from "@builder.io/qwik";
|
||||||
import { useCSSTransition } from "qwik-transition";
|
import { useCSSTransition } from "qwik-transition";
|
||||||
|
|
||||||
import Icon from "~/components/core/icon";
|
import Icon from "~/components/core/icon"; // Verifique se o caminho está correto
|
||||||
import type { Priority, Section, Checklist } from '../../types/PSC';
|
import type { Priority, Section, Checklist } from '../../types/PSC';
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
||||||
import styles from './psc.module.css';
|
import styles from './psc.module.css';
|
||||||
|
|
||||||
|
|
||||||
export default component$((props: { section: Section }) => {
|
export default component$((props: { section: Section }) => {
|
||||||
|
|
||||||
const [completed, setCompleted] = useLocalStorage('PSC_PROGRESS', {});
|
const [completed, setCompleted] = useLocalStorage('PSC_PROGRESS', {});
|
||||||
@ -56,7 +55,6 @@ export default component$((props: { section: Section }) => {
|
|||||||
return ignored.value[pointId] || false;
|
return ignored.value[pointId] || false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const isChecked = (pointId: string) => {
|
const isChecked = (pointId: string) => {
|
||||||
if (isIgnored(pointId)) return false;
|
if (isIgnored(pointId)) return false;
|
||||||
return completed.value[pointId] || false;
|
return completed.value[pointId] || false;
|
||||||
@ -104,14 +102,14 @@ export default component$((props: { section: Section }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSort = $((column: string) => {
|
// const handleSort = $((column: string) => {
|
||||||
if (sortState.column === column) { // Reverse direction if same column
|
// if (sortState.column === column) { // Reverse direction if same column
|
||||||
sortState.ascending = !sortState.ascending;
|
// sortState.ascending = !sortState.ascending;
|
||||||
} else { // Sort table by column
|
// } else { // Sort table by column
|
||||||
sortState.column = column;
|
// sortState.column = column;
|
||||||
sortState.ascending = true; // Default to ascending
|
// sortState.ascending = true; // Default to ascending
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
const resetFilters = $(() => {
|
const resetFilters = $(() => {
|
||||||
checklist.value = props.section.checklist;
|
checklist.value = props.section.checklist;
|
||||||
@ -121,7 +119,7 @@ export default component$((props: { section: Section }) => {
|
|||||||
filterState.show = originalFilters.show;
|
filterState.show = originalFilters.show;
|
||||||
});
|
});
|
||||||
|
|
||||||
const calculateProgress = (): { done: number, total: number, percent: number, disabled: number} => {
|
const calculateProgress = (): { done: number, total: number, percent: number, disabled: number } => {
|
||||||
let done = 0;
|
let done = 0;
|
||||||
let disabled = 0;
|
let disabled = 0;
|
||||||
let total = 0;
|
let total = 0;
|
||||||
@ -146,7 +144,6 @@ export default component$((props: { section: Section }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<div class="flex flex-wrap justify-between items-center">
|
<div class="flex flex-wrap justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<progress class="progress w-64" value={percent} max="100"></progress>
|
<progress class="progress w-64" value={percent} max="100"></progress>
|
||||||
@ -158,12 +155,12 @@ export default component$((props: { section: Section }) => {
|
|||||||
<div class="flex flex-wrap gap-2 justify-end my-4">
|
<div class="flex flex-wrap gap-2 justify-end my-4">
|
||||||
{(sortState.column || JSON.stringify(filterState) !== JSON.stringify(originalFilters)) && (
|
{(sortState.column || JSON.stringify(filterState) !== JSON.stringify(originalFilters)) && (
|
||||||
<button class="btn btn-sm hover:btn-primary" onClick$={resetFilters}>
|
<button class="btn btn-sm hover:btn-primary" onClick$={resetFilters}>
|
||||||
<Icon width={18} height={16} icon="clear"/>
|
<Icon width={18} height={16} icon="clear" />
|
||||||
Reset Filters
|
Reset Filters
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button class="btn btn-sm hover:btn-primary" onClick$={() => { showFilters.value = !showFilters.value; }}>
|
<button class="btn btn-sm hover:btn-primary" onClick$={() => { showFilters.value = !showFilters.value; }}>
|
||||||
<Icon width={18} height={16} icon="filters"/>
|
<Icon width={18} height={16} icon="filters" />
|
||||||
{showFilters.value ? 'Hide' : 'Show'} Filters
|
{showFilters.value ? 'Hide' : 'Show'} Filters
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -178,24 +175,24 @@ export default component$((props: { section: Section }) => {
|
|||||||
<label onClick$={() => (filterState.show = 'all')}
|
<label onClick$={() => (filterState.show = 'all')}
|
||||||
class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
<span class="text-sm">All</span>
|
<span class="text-sm">All</span>
|
||||||
<input type="radio" name="show" class="radio radio-sm checked:radio-info" checked />
|
<input type="radio" name="show" class="radio radio-sm checked:radio-info" checked={filterState.show === 'all'} />
|
||||||
</label>
|
</label>
|
||||||
<label onClick$={() => (filterState.show = 'remaining')}
|
<label onClick$={() => (filterState.show = 'remaining')}
|
||||||
class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
<span class="text-sm">Remaining</span>
|
<span class="text-sm">Remaining</span>
|
||||||
<input type="radio" name="show" class="radio radio-sm checked:radio-error" />
|
<input type="radio" name="show" class="radio radio-sm checked:radio-error" checked={filterState.show === 'remaining'} />
|
||||||
</label>
|
</label>
|
||||||
<label onClick$={() => (filterState.show = 'completed')}
|
<label onClick$={() => (filterState.show = 'completed')}
|
||||||
class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
<span class="text-sm">Completed</span>
|
<span class="text-sm">Completed</span>
|
||||||
<input type="radio" name="show" class="radio radio-sm checked:radio-success" />
|
<input type="radio" name="show" class="radio radio-sm checked:radio-success" checked={filterState.show === 'completed'} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{/* Filter by level */}
|
{/* Filter by level */}
|
||||||
<div class="flex justify-end items-center gap-1">
|
<div class="flex justify-end items-center gap-1">
|
||||||
<p class="font-bold text-sm">Filter</p>
|
<p class="font-bold text-sm">Filter</p>
|
||||||
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
<label class="p-2 rounded hover:bg-front transition-all cursor-pointer flex gap-2">
|
||||||
<span class="text-sm">Basic</span>
|
<span class="text-sm">Essential</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={filterState.levels.essential}
|
checked={filterState.levels.essential}
|
||||||
@ -226,45 +223,42 @@ export default component$((props: { section: Section }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<table class="table">
|
<div class="grid grid-cols-1 gap-4">
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{ [
|
|
||||||
{ id: 'done', text: 'Done?'},
|
|
||||||
{ id: 'advice', text: 'Advice' },
|
|
||||||
{ id: 'level', text: 'Level' }
|
|
||||||
].map((item) => (
|
|
||||||
<th
|
|
||||||
key={item.id}
|
|
||||||
class="cursor-pointer"
|
|
||||||
onClick$={() => handleSort(item.id)}
|
|
||||||
>
|
|
||||||
<span class="flex items-center gap-0.5 hover:text-primary transition">
|
|
||||||
<Icon width={12} height={14} icon="sort" />
|
|
||||||
{item.text}
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
<th>Details</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{filteredChecklist.sort(sortChecklist).map((item, index) => {
|
{filteredChecklist.sort(sortChecklist).map((item, index) => {
|
||||||
const badgeColor = getBadgeClass(item.priority);
|
const badgeColor = getBadgeClass(item.priority);
|
||||||
const itemId = generateId(item.point);
|
const itemId = generateId(item.point);
|
||||||
const isItemCompleted = isChecked(itemId);
|
const isItemCompleted = isChecked(itemId);
|
||||||
const isItemIgnored = isIgnored(itemId);
|
const isItemIgnored = isIgnored(itemId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={index} class={[
|
<details
|
||||||
'rounded-sm transition-all',
|
key={index}
|
||||||
isItemCompleted ? `bg-${badgeColor} bg-opacity-10` : '',
|
class={[
|
||||||
isItemIgnored? 'bg-neutral bg-opacity-15' : '',
|
'rounded-lg p-4 shadow transition-all',
|
||||||
|
isItemCompleted ? `bg-${badgeColor} bg-opacity-10` : 'bg-base-100',
|
||||||
|
isItemIgnored ? 'opacity-50' : '',
|
||||||
!isItemIgnored && !isItemCompleted ? `hover:bg-opacity-5 hover:bg-${badgeColor}` : '',
|
!isItemIgnored && !isItemCompleted ? `hover:bg-opacity-5 hover:bg-${badgeColor}` : '',
|
||||||
]}>
|
].join(' ')}
|
||||||
<td class="text-center">
|
>
|
||||||
|
<summary class="flex justify-between items-center cursor-pointer">
|
||||||
|
<div class="flex-1 flex items-center gap-2">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class={`badge font-[600] gap-2 badge-${badgeColor}`}>
|
||||||
|
{item.priority}
|
||||||
|
</div>
|
||||||
|
<label for={`done-${itemId}`} class={`text-base font-bold ${isIgnored(itemId) ? 'line-through' : ''}`}>
|
||||||
|
{item.point}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<Icon width={18} height={16} icon="chevron-down" class={`${styles.arrow} ${styles.summaryIcon}`} />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<label for={`done-${itemId}`} class="text-xs">Done</label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="radio"
|
||||||
class={`checkbox checked:checkbox-${badgeColor} hover:checkbox-${badgeColor}`}
|
name={`status-${itemId}`}
|
||||||
|
class={`radio radio-${badgeColor}`}
|
||||||
id={`done-${itemId}`}
|
id={`done-${itemId}`}
|
||||||
checked={isChecked(itemId)}
|
checked={isChecked(itemId)}
|
||||||
disabled={isIgnored(itemId)}
|
disabled={isIgnored(itemId)}
|
||||||
@ -274,11 +268,14 @@ export default component$((props: { section: Section }) => {
|
|||||||
setCompleted(data);
|
setCompleted(data);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label for={`ignore-${itemId}`} class="text-small block opacity-50 mt-2">Ignore</label>
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<label for={`ignore-${itemId}`} class="text-xs">Ignore</label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="radio"
|
||||||
|
name={`status-${itemId}`}
|
||||||
|
class={`radio radio-${badgeColor}`}
|
||||||
id={`ignore-${itemId}`}
|
id={`ignore-${itemId}`}
|
||||||
class={`toggle toggle-xs toggle-${badgeColor}`}
|
|
||||||
checked={isIgnored(itemId)}
|
checked={isIgnored(itemId)}
|
||||||
onClick$={() => {
|
onClick$={() => {
|
||||||
const ignoredData = ignored.value;
|
const ignoredData = ignored.value;
|
||||||
@ -290,25 +287,14 @@ export default component$((props: { section: Section }) => {
|
|||||||
setCompleted(completedData);
|
setCompleted(completedData);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<label
|
|
||||||
for={`done-${itemId}`}
|
|
||||||
class={`text-base font-bold ${isIgnored(itemId) ? 'line-through' : 'cursor-pointer'}`}>
|
|
||||||
{item.point}
|
|
||||||
</label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class={`badge gap-2 badge-${badgeColor}`}>
|
|
||||||
{item.priority}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td class={styles.checklistItemDescription} dangerouslySetInnerHTML={parseMarkdown(item.details)}></td>
|
</summary>
|
||||||
</tr>
|
<div class={styles.checklistItemDescription} dangerouslySetInnerHTML={parseMarkdown(item.details)}></div>
|
||||||
)}
|
</details>
|
||||||
)}
|
)
|
||||||
</tbody>
|
})}
|
||||||
</table>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user