Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ca 615 refactor breadcrumbs #1181

Merged
merged 58 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
fa1507b
Create breadcrumbs store
nas-tabchiche Dec 11, 2024
529849a
Write new breadcrumbs store
nas-tabchiche Dec 12, 2024
c60a6f1
Allow pushing multiple breadcrumbs at once
nas-tabchiche Dec 12, 2024
969ae4d
Provide sensible default values for pageTitle store
nas-tabchiche Dec 12, 2024
fafaebb
Use Anchor in EBIOS RM Tile component
nas-tabchiche Dec 12, 2024
0f8ed9f
Use Anchor across pages
nas-tabchiche Dec 12, 2024
f67bc80
Provide page title for requirement assessment update view
nas-tabchiche Dec 12, 2024
8f9cdde
Removed unused code
nas-tabchiche Dec 12, 2024
2add27a
Merge branch 'main' into CA-615-refactor-breadcrumbs
nas-tabchiche Dec 16, 2024
1044855
Make breadcrumbs a circular buffer
nas-tabchiche Dec 16, 2024
439b763
Make Anchor's default breadcrumbAction 'push'
nas-tabchiche Dec 16, 2024
3e79561
Set max width for breadcrumb items
nas-tabchiche Dec 16, 2024
30d3347
Explicitly use replace breadcrumbAction on side bar items
nas-tabchiche Dec 16, 2024
7e3fef8
Update import path for Anchor component
nas-tabchiche Dec 16, 2024
ab2d7a9
Use Anchor component in RO/TO detail view
nas-tabchiche Dec 16, 2024
7689066
Merge branch 'main' into CA-615-refactor-breadcrumbs
nas-tabchiche Dec 16, 2024
5d47b58
Trim breadcrumbs if current pathname is contained in store
nas-tabchiche Dec 16, 2024
6f1324f
Use Anchor in Calendar
nas-tabchiche Dec 16, 2024
10855ce
Manage stopPropagation
nas-tabchiche Dec 16, 2024
b68c274
Fix breadcrumb trimming
nas-tabchiche Dec 16, 2024
5847038
Update anchors for workshop 1
nas-tabchiche Dec 16, 2024
d796a00
Merge branch 'main' into CA-615-refactor-breadcrumbs
nas-tabchiche Dec 20, 2024
6f8b337
Update page title retrieval logic
nas-tabchiche Dec 20, 2024
cd66535
Move page title retrieval logic to breadcrumbs component
nas-tabchiche Dec 20, 2024
ba9e9a5
Attempt to get model verbose name from urlmodel on empty store
nas-tabchiche Dec 20, 2024
07f5861
chore: Remove unused code
nas-tabchiche Dec 20, 2024
36c924b
Use new breadcrumbs for analytics
nas-tabchiche Dec 20, 2024
73ff076
Remove breadcrumbObject store
nas-tabchiche Dec 20, 2024
6bc68fb
Merge branch 'main' into CA-615-refactor-breadcrumbs
nas-tabchiche Dec 20, 2024
f01137d
Use new breadcrumbs for X-Rays
nas-tabchiche Dec 20, 2024
0c1dc71
Use new breadcrumbs for My assignments
nas-tabchiche Dec 20, 2024
72531da
Write mergeCrumbs method
nas-tabchiche Dec 20, 2024
d55fc04
Generalize Anchor ccomponent to all non-generic detail views
nas-tabchiche Dec 20, 2024
d596d06
Remove page title test on edit views
nas-tabchiche Dec 20, 2024
c57c77b
Fix getPageTitle function
nas-tabchiche Dec 20, 2024
ac561b4
Merge branch 'main' into CA-615-refactor-breadcrumbs
nas-tabchiche Dec 20, 2024
1b5d400
Set title for libraries list view
nas-tabchiche Dec 20, 2024
831a3c6
Fix RO/TO edit button position
nas-tabchiche Dec 20, 2024
83f216f
Make breadcrumb href optional
nas-tabchiche Dec 20, 2024
f3609af
Add workshop in breadcrumbs
nas-tabchiche Dec 20, 2024
e8471b3
Improve getPageTitle function
nas-tabchiche Dec 20, 2024
c968ca2
Fix ebios-rm local name plural
nas-tabchiche Dec 20, 2024
993f866
Explicitly define page title
nas-tabchiche Dec 20, 2024
80ff07b
Improve RO/TO navigation
nas-tabchiche Dec 20, 2024
00d3009
Provide default value for breadcrumbAction in goto wrapper
nas-tabchiche Dec 20, 2024
88db7d0
Use goto wrapper for cancel buttons
nas-tabchiche Dec 20, 2024
3bd7c68
Explicitly define page title
nas-tabchiche Dec 20, 2024
8b85dff
Merge branch 'main' into CA-615-refactor-breadcrumbs
nas-tabchiche Dec 20, 2024
369c679
Explicitly define page title
nas-tabchiche Dec 20, 2024
4e94039
Add label to edit button Anchor tag
nas-tabchiche Dec 20, 2024
f1a1cfa
feat: add email title for users
Mohamed-Hacene Dec 21, 2024
03f4e81
feat: add email in breadcrumbs from table
Mohamed-Hacene Dec 21, 2024
023afc6
formatter
ab-smith Dec 21, 2024
a7fb63b
fix: use risk string for title
Mohamed-Hacene Dec 21, 2024
15123a7
fix: functional tests
Mohamed-Hacene Dec 21, 2024
6efa274
chore: format common test file
Mohamed-Hacene Dec 21, 2024
3fe07c0
fix: add str check in hasBreadcrumbPath
Mohamed-Hacene Dec 21, 2024
dc8e352
Use Anchor component on risk assessment detail
nas-tabchiche Dec 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions frontend/src/lib/components/Anchor/Anchor.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import { breadcrumbs, goto, type Breadcrumb } from '$lib/utils/breadcrumbs';

export let href = '';
export let breadcrumbAction: 'push' | 'replace' = 'push';
export let label = '';
export let prefixCrumbs: Breadcrumb[] = [];
export let stopPropagation: boolean = false;

const handleClick = (event) => {
const navLabel: string = label || event.target.innerText;
if (!navLabel) return;

const crumb = { label: navLabel, href };
const _crumbs = [...prefixCrumbs, crumb];
breadcrumbs[breadcrumbAction](_crumbs);

if (stopPropagation) {
event.stopPropagation();
event.preventDefault();
goto(href, { breadcrumbAction });
}
};
</script>

<a on:click={handleClick} {href} {...$$restProps}>
<slot />
</a>
100 changes: 53 additions & 47 deletions frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
Original file line number Diff line number Diff line change
@@ -1,59 +1,61 @@
<script lang="ts">
import { afterNavigate } from '$app/navigation';
import { page } from '$app/stores';
import { breadcrumbs, type Breadcrumb } from '$lib/utils/breadcrumbs';
import { URL_MODEL_MAP } from '$lib/utils/crud';
import { safeTranslate } from '$lib/utils/i18n';
import { pageTitle } from '$lib/utils/stores';

import { page } from '$app/stores';
import { toCamelCase } from '$lib/utils/locales';
import { breadcrumbObject, pageTitle } from '$lib/utils/stores';
import { listViewFields } from '$lib/utils/table';
import * as m from '$paraglide/messages';
async function trimBreadcrumbsToCurrentPath(
breadcrumbs: Breadcrumb[],
currentPath: string
): Promise<Breadcrumb[]> {
const idx = breadcrumbs.findIndex((c) => c.href === currentPath);
if (idx >= 0 && idx < breadcrumbs.length - 1) {
breadcrumbs = breadcrumbs.slice(0, idx + 1);
}
return breadcrumbs;
}

let crumbs: Array<{ label: string; href: string; icon?: string }> = [];
function getPageTitle(): string {
// Check each source in priority order
const title =
$page.data.title ??
$page.data.str ??
$page.data.name ??
getBreadcrumbTitle() ??
getUrlModelTitle();

const disableWorkhopLink = ['workshop1', 'workshop2', 'workshop3', 'workshop4', 'workshop5']; // Disable workshops links in breadcrumb
return safeTranslate(title);
}

$: {
// Remove zero-length tokens.
const tokens = $page.url.pathname.split('/').filter((t) => t !== '');
function getBreadcrumbTitle(): string | undefined {
return $breadcrumbs.length > 1 ? $breadcrumbs[$breadcrumbs.length - 1]?.label : undefined;
}

// Create { label, href } pairs for each token.
let tokenPath = '';
crumbs = tokens.map((t) => {
tokenPath += '/' + t;
if (t === $breadcrumbObject?.id) {
if ($breadcrumbObject.name) {
t = $breadcrumbObject.name;
} else if ($breadcrumbObject.first_name && $breadcrumbObject.last_name) {
t = `${$breadcrumbObject.first_name} ${$breadcrumbObject.last_name}`;
} else {
t = $breadcrumbObject.email;
}
} else if (t === 'folders') {
t = 'domains';
} else {
t = t.replace(/-/g, ' ');
t = toCamelCase(t);
}
return {
label: $page.data.label || t,
href:
Object.keys(listViewFields).includes(tokens[0]) &&
!listViewFields[tokens[0]].breadcrumb_link_disabled &&
!disableWorkhopLink.includes(t) // Disable workshops links in breadcrumb
? tokenPath
: null
};
});
function getUrlModelTitle(): string | undefined {
const lastPathSegment = $page.url.pathname.split('/').pop() as string;
return URL_MODEL_MAP[lastPathSegment]?.localNamePlural;
}

afterNavigate(async () => {
$breadcrumbs = await trimBreadcrumbsToCurrentPath($breadcrumbs, $page.url.pathname);
});

crumbs.unshift({ label: m.home(), href: '/', icon: 'fa-regular fa-compass' });
if (crumbs[crumbs.length - 1].label != 'edit') pageTitle.set(crumbs[crumbs.length - 1].label);
else pageTitle.set(m.edit() + ' ' + crumbs[crumbs.length - 2].label);
$: {
$pageTitle = getPageTitle();
if ($breadcrumbs.length < 2)
breadcrumbs.push([{ label: $pageTitle, href: $page.url.pathname }]);
}
</script>

<ol class="breadcrumb-nonresponsive">
{#each crumbs as c, i}
{#if i == crumbs.length - 1}
<span class="text-sm text-gray-500 font-semibold antialiased" data-testid="crumb-item">
<ol class="breadcrumb-nonresponsive h-6 overflow-hidden whitespace-nowrap">
{#each $breadcrumbs as c, i}
{#if i == $breadcrumbs.length - 1}
<span
class="max-w-[64ch] overflow-hidden text-sm text-gray-500 font-semibold antialiased"
data-testid="crumb-item"
>
{#if c.icon}
<i class={c.icon} />
{/if}
Expand All @@ -63,17 +65,21 @@
<li class="crumb">
{#if c.href}
<a
class="unstyled text-sm hover:text-primary-500 font-semibold antialiased whitespace-nowrap"
class="max-w-[64ch] overflow-hidden unstyled text-sm hover:text-primary-500 font-semibold antialiased whitespace-nowrap"
data-testid="crumb-item"
href={c.href}
on:click={() => breadcrumbs.slice(i)}
>
{#if c.icon}
<i class={c.icon} />
{/if}
{safeTranslate(c.label)}
</a>
{:else}
<span class="text-sm text-gray-500 font-semibold antialiased" data-testid="crumb-item">
<span
class="max-w-[64ch] overflow-hidden text-sm text-gray-500 font-semibold antialiased"
data-testid="crumb-item"
>
{#if c.icon}
<i class={c.icon} />
{/if}
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/lib/components/Calendar/Day.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import Anchor from '$lib/components/Anchor/Anchor.svelte';

export let day: number;
export let month: number;
Expand Down Expand Up @@ -34,9 +35,9 @@
<div class="flex flex-col justify-center h-full">
{#each displayInfo(day, month, year) as eta}
<li class="unstyled hover:underline">
<a href={eta[1]}>
<Anchor href={eta[1]}>
{eta[0]}
</a>
</Anchor>
</li>
{/each}
</div>
Expand All @@ -52,9 +53,9 @@
<div class="flex flex-col justify-center h-full">
{#each displayInfo(day, month, year) as eta}
<li class="unstyled hover:underline text-red-500">
<a href={eta[1]}>
<Anchor href={eta[1]}>
{eta[0]}
</a>
</Anchor>
</li>
{/each}
</div>
Expand All @@ -70,9 +71,9 @@
<div class="flex flex-col justify-center h-full">
{#each displayInfo(day, month, year) as eta}
<li class="unstyled hover:underline text-primary-500">
<a href={eta[1]}>
<Anchor href={eta[1]}>
{eta[0]}
</a>
</Anchor>
</li>
{/each}
</div>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/lib/components/DataViz/Article.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import Anchor from '$lib/components/Anchor/Anchor.svelte';

export let title;
export let desc;
export let link;
Expand All @@ -11,11 +13,11 @@
<div class="rounded-[10px] bg-white p-4 !pt-20 sm:p-6 h-full">
<div class="block text-xs text-gray-500 min-h-[2.5rem] flex items-end">{desc}</div>

<a href={link}>
<Anchor href={link}>
<h3 class="mt-0.5 text-lg font-medium text-gray-900">
{title}
</h3>
</a>
</Anchor>

<div class="mt-4 flex flex-wrap gap-1">
{#each tags as tag}
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/lib/components/DataViz/Card.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import Anchor from '$lib/components/Anchor/Anchor.svelte';

export let count: string = '0';
export let label: string;
export let href: string = '#';
Expand All @@ -13,8 +15,9 @@
export let customClass: string = '';
</script>

<a
<Anchor
{href}
{label}
class="flex flex-col shadow-lg text-purple-800 p-2 h-28 bg-white hover:bg-violet-50 {cEmphasis} {customClass}"
>
<div class="text-xs">
Expand All @@ -25,7 +28,7 @@
<p class="text-4xl font-bold text-left">{count}</p>
<div class="text-sm">{label}</div>
</div>
</a>
</Anchor>

<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
Expand Down
55 changes: 31 additions & 24 deletions frontend/src/lib/components/DetailView/DetailView.svelte
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
<script lang="ts">
import { safeTranslate } from '$lib/utils/i18n';
import { page } from '$app/stores';
import Anchor from '$lib/components/Anchor/Anchor.svelte';
import List from '$lib/components/List/List.svelte';
import ConfirmModal from '$lib/components/Modals/ConfirmModal.svelte';
import CreateModal from '$lib/components/Modals/CreateModal.svelte';
import MissingConstraintsModal from '$lib/components/Modals/MissingConstraintsModal.svelte';
import ModelTable from '$lib/components/ModelTable/ModelTable.svelte';
import { ISO_8601_REGEX } from '$lib/utils/constants';
import { URL_MODEL_MAP, checkConstraints } from '$lib/utils/crud';
import { getModelInfo } from '$lib/utils/crud.js';
import { formatDateOrDateTime } from '$lib/utils/datetime';
import { isURL } from '$lib/utils/helpers';
import { safeTranslate } from '$lib/utils/i18n';
import { toCamelCase } from '$lib/utils/locales.js';
import * as m from '$paraglide/messages.js';
import { languageTag } from '$paraglide/runtime.js';
import type {
ModalComponent,
ModalSettings,
ModalStore,
ToastStore
} from '@skeletonlabs/skeleton';
import { TabGroup, Tab, getModalStore, getToastStore } from '@skeletonlabs/skeleton';
import { breadcrumbObject } from '$lib/utils/stores';
import { superForm } from 'sveltekit-superforms';
import { getModelInfo } from '$lib/utils/crud.js';
import { URL_MODEL_MAP } from '$lib/utils/crud';
import { isURL } from '$lib/utils/helpers';
import { toCamelCase } from '$lib/utils/locales.js';
import { checkConstraints } from '$lib/utils/crud';
import { languageTag } from '$paraglide/runtime.js';
import * as m from '$paraglide/messages.js';
import { ISO_8601_REGEX } from '$lib/utils/constants';
import { formatDateOrDateTime } from '$lib/utils/datetime';
import List from '$lib/components/List/List.svelte';
import { SECURITY_OBJECTIVE_SCALE_MAP } from '$lib/utils/constants';
import { Tab, TabGroup, getModalStore, getToastStore } from '@skeletonlabs/skeleton';

import { onMount } from 'svelte';
import { goto } from '$app/navigation';

const modalStore: ModalStore = getModalStore();
const toastStore: ToastStore = getToastStore();
Expand All @@ -51,8 +47,6 @@
);
}

$: breadcrumbObject.set(data.data);

let tabSet = 0;

function handleFormUpdated({
Expand Down Expand Up @@ -288,7 +282,9 @@
{#if value !== null && value !== undefined && value !== ''}
{#if key === 'library'}
{@const itemHref = `/libraries/${value.id}?loaded`}
<a href={itemHref} class="anchor">{value.name}</a>
<Anchor breadcrumbAction="push" href={itemHref} class="anchor"
>{value.name}</Anchor
>
{:else if key === 'severity'}
<!-- We must add translations for the following severity levels -->
<!-- Is this a correct way to convert the severity integer to the stringified security level ? -->
Expand All @@ -309,7 +305,9 @@
(item) => item.field === key
)?.urlModel
}/${val.id}`}
<a href={itemHref} class="anchor">{val.str}</a>
<Anchor breadcrumbAction="push" href={itemHref} class="anchor"
>{val.str}</Anchor
>
{:else if val.str}
{val.str}
{:else}
Expand All @@ -327,7 +325,9 @@
(item) => item.field === key
)?.urlModel
}/${value.id}`}
<a href={itemHref} class="anchor">{value.str}</a>
<Anchor breadcrumbAction="push" href={itemHref} class="anchor"
>{value.str}</Anchor
>
<!-- Shortcut before DetailView refactoring -->
{:else if value === 'P1'}
<li class="fa-solid fa-flag text-red-500"></li>
Expand All @@ -342,7 +342,9 @@
<li class="fa-solid fa-flag text-gray-500"></li>
{m.p4()}
{:else if isURL(value) && !value.startsWith('urn')}
<a href={value} target="_blank" class="anchor">{value}</a>
<Anchor breadcrumbAction="push" href={value} target="_blank" class="anchor"
>{value}</Anchor
>
{:else if ISO_8601_REGEX.test(value) && (key === 'created_at' || key === 'updated_at' || key === 'expiry_date' || key === 'accepted_at' || key === 'rejected_at' || key === 'revoked_at' || key === 'eta')}
{formatDateOrDateTime(value, languageTag())}
{:else if m[toCamelCase(value.str || value.name)]}
Expand Down Expand Up @@ -384,10 +386,15 @@
{/if}
{#if displayEditButton()}
<div class="flex flex-col space-y-2 ml-4">
<a
<Anchor
breadcrumbAction="push"
href={`${$page.url.pathname}/edit?next=${$page.url.pathname}`}
label={m.edit()}
class="btn variant-filled-primary h-fit"
><i class="fa-solid fa-pen-to-square mr-2" data-testid="edit-button" />{m.edit()}</a
><i
class="fa-solid fa-pen-to-square mr-2"
data-testid="edit-button"
/>{m.edit()}</Anchor
>
{#if data.urlModel === 'applied-controls'}
<span class="pt-4 font-light text-sm">Power-ups:</span>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/components/Forms/ModelForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import FilteringLabelForm from './ModelForm/FilteringLabelForm.svelte';
import OperationalScenarioForm from './ModelForm/OperationalScenarioForm.svelte';
import StrategicScenarioForm from './ModelForm/StrategicScenarioForm.svelte';
import { goto } from '$lib/utils/breadcrumbs';

export let form: SuperValidated<AnyZodObject>;
export let invalidateAll = true; // set to false to keep form data using muliple forms on a page
Expand All @@ -70,7 +71,7 @@
var currentUrl = window.location.href;
var url = new URL(currentUrl);
var nextValue = getSecureRedirect(url.searchParams.get('next'));
if (nextValue) window.location.href = nextValue;
if (nextValue) goto(nextValue);
}
}
$: shape = schema.shape || schema._def.schema.shape;
Expand Down
Loading
Loading