Adds reccomended software functionality to sections

This commit is contained in:
Alicia Sykes 2024-02-10 15:18:49 +00:00
parent 9439d7f490
commit 81e77ed22d
5 changed files with 87 additions and 18 deletions

View File

@ -119,21 +119,52 @@ export default component$((props: { section: Section }) => {
filterState.show = originalFilters.show;
});
const calculateProgress = (): { done: number, total: number, percent: number, disabled: number} => {
let done = 0;
let disabled = 0;
let total = 0;
props.section.checklist.forEach((item) => {
const itemId = generateId(item.point);
if (isIgnored(itemId)) {
disabled += 1;
} else if (isChecked(itemId)) {
done += 1;
total += 1;
} else {
total += 1;
}
});
const percent = Math.round((done / total) * 100);
return { done, total: props.section.checklist.length, percent, disabled };
};
const { done, total, percent, disabled } = calculateProgress();
return (
<>
<div class="flex flex-wrap justify-between items-center">
<div>
<progress class="progress w-64" value={percent} max="100"></progress>
<p class="text-xs text-center">
{done} out of {total} ({percent}%)
complete, {disabled} ignored</p>
</div>
<div class="flex flex-wrap gap-2 justify-end my-4">
{(sortState.column || JSON.stringify(filterState) !== JSON.stringify(originalFilters)) && (
<button class="btn btn-sm hover:btn-primary" onClick$={resetFilters}>
<Icon width={18} height={16} icon="clear"/>
Reset Filters
<div class="flex flex-wrap gap-2 justify-end my-4">
{(sortState.column || JSON.stringify(filterState) !== JSON.stringify(originalFilters)) && (
<button class="btn btn-sm hover:btn-primary" onClick$={resetFilters}>
<Icon width={18} height={16} icon="clear"/>
Reset Filters
</button>
)}
<button class="btn btn-sm hover:btn-primary" onClick$={() => { showFilters.value = !showFilters.value; }}>
<Icon width={18} height={16} icon="filters"/>
{showFilters.value ? 'Hide' : 'Show'} Filters
</button>
)}
<button class="btn btn-sm hover:btn-primary" onClick$={() => { showFilters.value = !showFilters.value; }}>
<Icon width={18} height={16} icon="filters"/>
{showFilters.value ? 'Hide' : 'Show'} Filters
</button>
</div>
</div>
{showFilters.value && (

View File

@ -1,6 +1,6 @@
// src/routes/articles/[slug].tsx
import { component$, Resource, useResource$, useStore } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';
import { type DocumentHead, useLocation, useDocumentHead } from '@builder.io/qwik-city';
import { marked } from "marked";
import articles from '~/data/articles';
@ -14,6 +14,20 @@ export default component$(() => {
const slug = location.params.slug;
const article = articles.find(a => a.slug === slug);
// useDocumentHead(() => {
// if (!article) {
// return {
// title: '404 Not Found | Your Site Name',
// meta: [{ name: 'description', content: 'The requested article was not found.' }],
// };
// }
// return {
// title: `${article.title} | Your Site Name`,
// meta: [{ name: 'description', content: article.description }],
// };
// });
const parseMarkdown = (text: string | undefined): string => {
if (!text) return '';
@ -98,3 +112,13 @@ export default component$(() => {
);
});
export const head: DocumentHead = {
title: "Article | Digital Defense",
meta: [
{
name: "description",
content: "",
},
],
};

View File

@ -4,19 +4,17 @@ import { marked } from 'marked';
import Icon from '~/components/core/icon';
import { ChecklistContext } from '~/store/checklist-context';
import { useChecklist } from '~/store/local-checklist-store';
import type { Section } from "~/types/PSC";
import Table from '~/components/psc/checklist-table';
export default component$(() => {
const checklists = useContext(ChecklistContext);
const localChecklist = useChecklist();
const loc = useLocation();
const slug = loc.params.title;
const section: Section | undefined = (localChecklist.checklist.checklist || checklists.value)
const section: Section | undefined = (checklists.value)
.find((item: Section) => item.slug === slug);
const parseMarkdown = (text: string | undefined): string => {
@ -36,6 +34,20 @@ export default component$(() => {
{section && (<Table section={section} />)}
</div>
{section && section.softwareLinks && (
<>
<div class="divider my-4">Useful Links</div>
<h3 class="text-xl my-2">Recommended Software</h3>
<ul class="list-disc pl-4">
{section.softwareLinks.map((link, index) => (
<li key={index}>
<a class="link link-primary" href={link.url} title={link.description}>{link.title}</a>
</li>
))}
</ul>
</>
)}
</article>
</div>
);

View File

@ -8,7 +8,7 @@ import { ChecklistContext } from "~/store/checklist-context";
import type { Sections } from "~/types/PSC";
export const useChecklists = routeLoader$(async () => {
const remoteUrl = 'https://gist.githubusercontent.com/Lissy93/0c26e4255b6fabc2c027ac72a4428aeb/raw/4ccdbc71e0fffdef53472cf98acbe40b0acf982b/personal-security-checklist.yml';
const remoteUrl = 'https://gist.githubusercontent.com/Lissy93/0c26e4255b6fabc2c027ac72a4428aeb/raw/c36fc0430df223534eaf76c035943f4b343915e4/personal-security-checklist.yml';
// TODO: Update this URL to point to the Git repository
return fetch(remoteUrl)
.then((res) => res.text())

View File

@ -13,7 +13,9 @@ export interface Section {
icon: string,
color: string,
checklist: Checklist[],
furtherLinks?: FurtherLink[],
softwareLinks?: Link[],
helpfulTools?: Link[],
furtherResources?: Link[],
}
export type Priority = 'recommended' | 'optional' | 'advanced';
@ -24,8 +26,8 @@ export interface Checklist {
details: string,
}
export interface FurtherLink {
export interface Link {
title: string,
url: string,
description: string,
description?: string,
}