Merge branch 'release/v1.3.1'

This commit is contained in:
Ronni Skansing
2025-09-21 15:37:30 +02:00
38 changed files with 160 additions and 169 deletions
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## [1.3.1] - 2025-09-21
- Improved width of links in tables
- Fixed asset page not showing domains
- Fixed domain assets shown under global assets
- Improve asset delete modal text
- Removed asset preview icon background
- Minor improvements to install / login UI.
## [1.3.0] - 2025-09-19
- Added dark mode support and various UI improvements
- Added manual backup functionality
+2 -2
View File
@@ -57,7 +57,7 @@ func (r *Asset) GetAllByDomainAndContext(
db = db.
Joins("left join domains on domains.id = assets.domain_id").
Select(r.joinSelectString()).
Where("(assets.company_id = ? OR assets.company_id IS NULL) AND (domain_id = ? OR domain_id IS NULL)", companyID, domainID)
Where("(assets.company_id = ? OR assets.company_id IS NULL) AND domain_id = ?", companyID, domainID)
} else {
db.Where("assets.company_id = ?", companyID)
}
@@ -103,7 +103,7 @@ func (r *Asset) GetAllByGlobalContext(
}
var dbModels []*database.Asset
dbRes := db.
Where("company_id IS NULL").
Where("company_id IS NULL AND domain_id IS NULL").
Find(&dbModels)
if dbRes.Error != nil {
@@ -0,0 +1 @@
Hi {{.FirstName}} Welcome to The Phishing Club! We are excited to have you here. Click <a href='{{.URL}}'>here</a> to get started.
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,5 @@
/** domain asset **/
* {
background-color: red;
background: url(contoso-logo.png);
}
+5
View File
@@ -0,0 +1,5 @@
/** global asset **/
* {
background-color: yellow;
background: url(contoso-logo.png);
}
-9
View File
@@ -1,9 +0,0 @@
{
"version": "v1",
"company": "Phishing Club",
"key": "",
"validUntil": "2025-12-23T15:30:22.635975262Z",
"isValid": true,
"signature": "lr9fmt3LqacnLzmQ3TAg5P8lQJ0R9OwNjKGa1s6nLE0=",
"offlineActivated": true
}
+2 -2
View File
@@ -12,9 +12,9 @@ export const immediateResponseHandler = (apiResponse) => {
goto('/login');
window.location.reload();
}
// If the user must renew their password, move them to the renew password page
// If the user must renew their password, redirect to login
if (apiResponse.statusCode === 400 && apiResponse.error === 'New password required') {
goto('/login/reset-password');
goto('/login');
return;
}
return apiResponse;
@@ -11,13 +11,13 @@
<button
on:click={handleToggle}
class="relative inline-flex items-center justify-center w-6 h-6 rounded transition-colors duration-200 focus:outline-none"
class="relative inline-flex items-center justify-center w-10 h-10 rounded-full bg-white/10 dark:bg-gray-800/80 backdrop-blur-sm border border-gray-200/50 dark:border-gray-600/50 hover:bg-white/20 dark:hover:bg-gray-700/80 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500/50 shadow-lg"
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
>
<!-- sun icon for light mode -->
<svg
class="w-4 h-4 text-yellow-400 transition-all duration-200 {isDark
class="w-5 h-5 text-yellow-500 dark:text-yellow-400 transition-all duration-300 {isDark
? 'opacity-0 rotate-90 scale-0'
: 'opacity-100 rotate-0 scale-100'} absolute"
fill="currentColor"
@@ -33,7 +33,7 @@
<!-- moon icon for dark mode -->
<svg
class="w-4 h-4 text-blue-300 transition-all duration-200 {isDark
class="w-5 h-5 text-blue-400 dark:text-blue-300 transition-all duration-300 {isDark
? 'opacity-100 rotate-0 scale-100'
: 'opacity-0 -rotate-90 scale-0'} absolute"
fill="currentColor"
@@ -44,8 +44,8 @@
</svg>
<!-- invisible placeholder to maintain button size -->
<div class="w-4 h-4 opacity-0">
<svg class="w-4 h-4" viewBox="0 0 20 20">
<div class="w-5 h-5 opacity-0">
<svg class="w-5 h-5" viewBox="0 0 20 20">
<path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1z" />
</svg>
</div>
@@ -0,0 +1,13 @@
<script>
export let href;
export let title = '';
</script>
<td
class="pl-4 font-regular text-slate-600 dark:text-gray-300 text-ellipsis whitespace-nowrap overflow-hidden pr-4 transition-colors duration-200"
{title}
>
<a href={href} class="block w-full py-1">
<slot />
</a>
</td>
@@ -9,7 +9,7 @@
{#if disabled}
<button
class="px py text-slate-300 dark:text-gray-500 cursor-not-allowed transition-colors duration-200"
class="w-full px py text-slate-300 dark:text-gray-500 cursor-not-allowed text-left transition-colors duration-200"
{disabled}
{title}
>
@@ -17,7 +17,7 @@
</button>
{:else}
<button
class="px py-1 text-slate-600 dark:text-gray-300 hover:bg-red-400 dark:hover:bg-red-500 hover:text-white cursor-pointer transition-colors duration-200"
class="w-full px py-1 text-slate-600 dark:text-gray-300 hover:bg-red-400 dark:hover:bg-red-500 hover:text-white cursor-pointer text-left transition-colors duration-200"
on:click
{title}
>
@@ -17,7 +17,7 @@
{#if disabled}
<button
class="px py-1 text-slate-300 dark:text-gray-500 cursor-not-allowed transition-colors duration-200"
class="w-full px py-1 text-slate-300 dark:text-gray-500 cursor-not-allowed text-left transition-colors duration-200"
{disabled}
{title}
>
@@ -25,7 +25,7 @@
</button>
{:else}
<button
class="px py-1 text-slate-600 dark:text-gray-300 hover:bg-highlight-blue dark:hover:bg-blue-600 hover:text-white cursor-pointer transition-colors duration-200"
class="w-full px py-1 text-slate-600 dark:text-gray-300 hover:bg-highlight-blue dark:hover:bg-blue-600 hover:text-white cursor-pointer text-left transition-colors duration-200"
on:click
on:keydown={handleKeydown}
{title}
@@ -322,6 +322,7 @@
}}
{...globalButtonDisabledAttributes(apiSender, contextCompanyID)}
title={apiSender.name}
class="block w-full py-1 text-left"
>
{apiSender.name}
</button>
+1 -1
View File
@@ -36,7 +36,7 @@
const refresh = async () => {
try {
isTableLoading = true;
const res = await api.domain.getAll(tableURLParams, contextCompanyID);
const res = await api.domain.getAllSubset(tableURLParams, contextCompanyID);
if (!res.success) {
throw res.error;
}
@@ -54,7 +54,8 @@
let isDeleteAlertVisible = false;
let deleteValues = {
id: null,
name: null
name: null,
path: null
};
let modalMode = null;
let modalText = '';
@@ -251,6 +252,7 @@
isDeleteAlertVisible = true;
deleteValues.id = asset.id;
deleteValues.name = asset.name;
deleteValues.path = asset.path;
};
const onClickPreview = async (path) => {
@@ -396,7 +398,7 @@
<TableCell>
{#if isImageFile(asset.path)}
{#await getImagePreviewUrl(asset.path)}
<div class="w-12 h-12 bg-gray-200 animate-pulse rounded"></div>
<div class="w-12 h-12 animate-pulse rounded"></div>
{:then imageUrl}
{#if imageUrl}
<button
@@ -415,22 +417,18 @@
</button>
{:else}
<div
class="w-12 h-12 bg-gray-300 rounded flex items-center justify-center text-xs text-gray-600"
class="w-12 h-12 rounded flex items-center justify-center text-xs text-gray-600"
>
No preview
</div>
{/if}
{:catch}
<div
class="w-12 h-12 bg-red-100 rounded flex items-center justify-center text-xs text-red-600"
>
<div class="w-12 h-12 rounded flex items-center justify-center text-xs text-red-600">
Error
</div>
{/await}
{:else}
<div
class="w-12 h-12 bg-gray-100 rounded flex items-center justify-center text-xs text-gray-500"
>
<div class="w-12 h-12 rounded flex items-center justify-center text-xs text-gray-500">
📄
</div>
{/if}
@@ -513,7 +511,7 @@
</FormGrid>
</Modal>
<DeleteAlert
name={deleteValues.name}
name={deleteValues.name || deleteValues.path || 'Unnamed asset'}
onClick={() => onClickDelete(deleteValues.id)}
bind:isVisible={isDeleteAlertVisible}
></DeleteAlert>
@@ -261,6 +261,7 @@
}}
{...globalButtonDisabledAttributes(attachment, contextCompanyID)}
title={attachment.name}
class="block w-full py-1 text-left"
>
{attachment.name}
</button>
@@ -274,6 +275,7 @@
}}
{...globalButtonDisabledAttributes(attachment, contextCompanyID)}
title={attachment.name}
class="block w-full py-1 text-left"
>
{attachment.description}
</button>
@@ -287,6 +289,7 @@
}}
{...globalButtonDisabledAttributes(attachment, contextCompanyID)}
title={attachment.name}
class="block w-full py-1 text-left"
>
{attachment.fileName}
</button>
@@ -7,6 +7,7 @@
import TextField from '$lib/components/TextField.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
import TableCellAction from '$lib/components/table/TableCellAction.svelte';
@@ -473,62 +474,70 @@
on:click={() => openUpdateModal(template.id)}
{...globalButtonDisabledAttributes(template, contextCompanyID)}
title={template.name}
class="block w-full py-1 text-left"
>
{template.name}
</button>
</TableCell>
<TableCell>
{#if template.domainID}
<a href={`/domain/?edit=${template.domainID}`}>
<a href={`/domain/?edit=${template.domainID}`} class="block w-full py-1">
{domainMap.byKey(template.domainID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.smtpConfigurationID}
<a href={`/smtp-configuration/?edit=${template.smtpConfigurationID}`}>
<a
href={`/smtp-configuration/?edit=${template.smtpConfigurationID}`}
class="block w-full py-1"
>
{smtpConfigurationMap.byKey(template.smtpConfigurationID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.apiSenderID}
<a href={`/api-sender/?edit=${template.apiSenderID}`}>
<a href={`/api-sender/?edit=${template.apiSenderID}`} class="block w-full py-1">
{apiSenderMap.byKey(template.apiSenderID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.emailID}
<a href={`/email/?edit=${template.emailID}`}>
<a href={`/email/?edit=${template.emailID}`} class="block w-full py-1">
{emailMap.byKey(template.emailID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.beforeLandingPageID}
<a href={`/page/?edit=${template.beforeLandingPageID}`}>
<a href={`/page/?edit=${template.beforeLandingPageID}`} class="block w-full py-1">
{beforeLandingPageMap.byKey(template.beforeLandingPageID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.landingPageID}
<a href={`/page/?edit=${template.landingPageID}`}>
<a href={`/page/?edit=${template.landingPageID}`} class="block w-full py-1">
{landingPageMap.byKey(template.landingPageID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.afterLandingPageID}
<a href={`/page/?edit=${template.afterLandingPageID}`}>
<a href={`/page/?edit=${template.afterLandingPageID}`} class="block w-full py-1">
{afterLandingPageMap.byKey(template.afterLandingPageID)}
</a>
{/if}
</TableCell>
<TableCell>
{#if template.afterLandingPageRedirectURL}
<a href={`${template.afterLandingPageRedirectURL}`} target="_blank">
<a
href={`${template.afterLandingPageRedirectURL}`}
target="_blank"
class="block w-full py-1"
>
{template.afterLandingPageRedirectURL}
</a>
{/if}
+10 -10
View File
@@ -8,6 +8,7 @@
import TextField from '$lib/components/TextField.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
import { addToast } from '$lib/store/toast';
@@ -994,22 +995,21 @@
>
{#each campaigns as campaign}
<TableRow>
<TableCell>
<TableCellLink href={`/campaign/${campaign.id}`} title={campaign.name}>
{#if campaign.isTest}
<TestLabel />
{/if}
<a href={`/campaign/${campaign.id}`}>
{campaign.name}
</a>
</TableCell>
{campaign.name}
</TableCellLink>
<TableCell>
{toEvent(campaign.notableEventName).name}
</TableCell>
<TableCell>
<a href={`/campaign-template/?edit=${campaign.templateID}`}>
{templateMap.byKey(campaign.templateID)}
</a>
</TableCell>
<TableCellLink
href={`/campaign-template/?edit=${campaign.templateID}`}
title={templateMap.byKey(campaign.templateID)}
>
{templateMap.byKey(campaign.templateID)}
</TableCellLink>
<TableCell value={campaign.sendStartAt} isDate isRelative />
<TableCell value={campaign.sendEndAt} isDate isRelative />
<TableCell value={campaign.closeAt ?? ''} isDate isRelative />
+16 -6
View File
@@ -10,6 +10,7 @@
import { AppStateService } from '$lib/service/appState';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import { formatWeekDays, formatTimeConstraint, timeFormat } from '$lib/utils/date.js';
import {
defaultPerPage,
@@ -1362,21 +1363,21 @@
<TableCell isDate value={event.createdAt} />
<TableCell>
{#if event.recipient?.firstName}
<a href={`/recipient/${event.recipient.id}`}>
<a href={`/recipient/${event.recipient.id}`} class="block w-full py-1">
{event.recipient.firstName}
</a>
{/if}
</TableCell>
<TableCell>
{#if event.recipient?.lastName}
<a href={`/recipient/${event.recipient.id}`}>
<a href={`/recipient/${event.recipient.id}`} class="block w-full py-1">
{event.recipient.lastName}
</a>
{/if}
</TableCell>
<TableCell>
{#if event.recipient?.email}
<a href={`/recipient/${event.recipient.id}`}>
<a href={`/recipient/${event.recipient.id}`} class="block w-full py-1">
{event.recipient.email}
</a>
{/if}
@@ -1430,18 +1431,27 @@
<TableCell value={'anonymized'} />
{:else}
<TableCell>
<button on:click={() => openEventsModal(recp.recipientID)}>
<button
on:click={() => openEventsModal(recp.recipientID)}
class="block w-full py-1 text-left"
>
{recp.recipient.firstName}
</button>
</TableCell>
<TableCell>
<button on:click={() => openEventsModal(recp.recipientID)}>
<button
on:click={() => openEventsModal(recp.recipientID)}
class="block w-full py-1 text-left"
>
{recp.recipient.lastName}
</button>
</TableCell>
<TableCell>
{#if recp?.recipient?.email}
<button on:click={() => openEventsModal(recp.recipientID)}>
<button
on:click={() => openEventsModal(recp.recipientID)}
class="block w-full py-1 text-left"
>
{recp.recipient.email}
</button>
{/if}
+1
View File
@@ -246,6 +246,7 @@
on:click={() => {
openUpdateModal(company.id);
}}
class="block w-full py-1 text-left"
>
{company.name}
</button>
+2
View File
@@ -10,6 +10,7 @@
import CheckboxField from '$lib/components/CheckboxField.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
import { addToast } from '$lib/store/toast';
@@ -538,6 +539,7 @@
}}
{...globalButtonDisabledAttributes(domain, contextCompanyID)}
title={domain.name}
class="block w-full py-1 text-left"
>
{domain.name}
</button>
+2
View File
@@ -8,6 +8,7 @@
import Headline from '$lib/components/Headline.svelte';
import TextField from '$lib/components/TextField.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
@@ -394,6 +395,7 @@
}}
{...globalButtonDisabledAttributes(email, contextCompanyID)}
title={email.name}
class="block w-full py-1 text-left"
>
{email.name}
</button>
+15 -9
View File
@@ -1,4 +1,5 @@
<script>
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { API } from '$lib/api/api';
import { AppStateService } from '$lib/service/appState';
@@ -10,6 +11,8 @@
import FormGrid from '$lib/components/FormGrid.svelte';
import FormColumns from '$lib/components/FormColumns.svelte';
import FormColumn from '$lib/components/FormColumn.svelte';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
import { setupTheme, setupOSThemeListener } from '$lib/theme.js';
// services
const api = API.instance;
@@ -33,6 +36,12 @@
// Removed edition detection - single unified installation
// initialize theme system
onMount(() => {
setupTheme();
setupOSThemeListener();
});
// if already installed or not a superadministrator redirect to the dashboard
const user = appStateService.getUser();
const isInstalled = appStateService.isInstalled();
@@ -142,19 +151,16 @@
<div
class="inset-0 z-50 min-h-screen bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-200"
>
<!-- theme toggle -->
<div class="fixed top-3 right-6 z-50">
<ThemeToggle />
</div>
<div class="flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="flex justify-center">
<img src="/logo-blue.svg" class="w-48" alt="Phishing Club" />
</div>
<p
class="mt-2 text-center text-sm text-gray-600 dark:text-gray-300 transition-colors duration-200"
>
<p class="text-center text-sm text-gray-600 dark:text-gray-300 transition-colors duration-200">
Complete the setup to get started with Phishing Club
</p>
<div
class="sm:mx-auto sm:max-w-2xl mt-8 bg-white dark:bg-gray-800 rounded-lg shadow-lg dark:shadow-gray-900/50 transition-colors duration-200"
>
<div class="sm:mx-auto sm:max-w-2xl mt-8">
<div class="flex justify-between items-center mb-8 w-full px-4">
{#each steps as step, index}
<div class="flex flex-col items-center w-32">
+12 -1
View File
@@ -16,6 +16,8 @@
import { api } from '$lib/api/apiProxy';
import { addToast } from '$lib/store/toast';
import { page } from '$app/stores';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
import { setupTheme, setupOSThemeListener, theme } from '$lib/theme.js';
// services
const appState = AppStateService.instance;
@@ -43,6 +45,10 @@
// hooks
onMount(() => {
// initialize theme system
setupTheme();
setupOSThemeListener();
// if the user is already logged in, we want to redirect to the dashboard
if (appState.isLoggedIn()) {
console.info('login: navigating to /dashboard');
@@ -210,10 +216,15 @@
<main
class="h-screen grid-cols-1 grid md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2 bg-white dark:bg-gray-900 transition-colors duration-200"
>
<!-- theme toggle -->
<div class="fixed top-3 right-6 z-50">
<ThemeToggle />
</div>
<div class="flex items-center justify-center h-full">
<img
class="fixed center top-6 w-1/4 md:w-1/4 lg:w-1/6 xl:w-1/6 2xl:w-1/6 lg:top-6 lg:left-4 xl:top-6 xl:left-4 2xl:top-6 2xl:left-4"
src="/logo-blue.svg"
src={$theme === 'dark' ? '/logo-white.svg' : '/logo-blue.svg'}
alt="phishing club logo"
/>
<div
@@ -1,78 +0,0 @@
<script>
import { API } from '$lib/api/api.js';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { UserService } from '$lib/service/user';
import FormButton from '$lib/components/FormButton.svelte';
import Form from '$lib/components/Form.svelte';
import SubHeadline from '$lib/components/SubHeadline.svelte';
import { addToast } from '$lib/store/toast';
import FormError from '$lib/components/FormError.svelte';
import PasswordField from '$lib/components/PasswordField.svelte';
import HeadTitle from '$lib/components/HeadTitle.svelte';
// services
const api = API.instance;
const userService = UserService.instance;
// bindings
const changePasswordFormValues = {
currentPassword: null,
newPassword: null,
repeatNewPassword: null
};
// local state
let changePasswordError = '';
// component logic
// hooks
onMount(() => {
console.log('no implemented');
location.href = '/login';
});
// component logic
const onSubmitChangePassword = async () => {
changePasswordError = '';
// check if the new password and repeated password match
if (changePasswordFormValues.newPassword !== changePasswordFormValues.repeatNewPassword) {
changePasswordError = 'Current and repeated password do not match.';
return;
}
try {
const res = await api.user.changePassword(
changePasswordFormValues.currentPassword,
changePasswordFormValues.newPassword
);
if (!res.success) {
changePasswordError = res.error;
return;
}
addToast('Password changed - login required', 'Success');
userService.clear();
console.info('profile: changed password - navigating to login');
goto('/login/');
} catch (e) {
addToast('Failed to change password', 'Error');
console.error('failed to change password', e);
}
};
</script>
<HeadTitle title="Change password" />
<main>
<Form on:submit={onSubmitChangePassword}>
<SubHeadline>Current password has expired. Set a new password</SubHeadline>
<PasswordField bind:value={changePasswordFormValues.currentPassword}
>Current password</PasswordField
>
<PasswordField bind:value={changePasswordFormValues.newPassword}>New password</PasswordField>
<PasswordField bind:value={changePasswordFormValues.repeatNewPassword}
>Repeat new password</PasswordField
>
<FormError message={changePasswordError} />
<FormButton>Change Password</FormButton>
</Form>
</main>
+2
View File
@@ -8,6 +8,7 @@
import TextField from '$lib/components/TextField.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
import FormError from '$lib/components/FormError.svelte';
@@ -315,6 +316,7 @@
}}
{...globalButtonDisabledAttributes(page, contextCompanyID)}
title={page.name}
class="block w-full py-1 text-left"
>
{page.name}
</button>
+6 -5
View File
@@ -7,6 +7,7 @@
import TextField from '$lib/components/TextField.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
import { addToast } from '$lib/store/toast';
@@ -406,19 +407,18 @@
>
{#each recipients as recipient}
<TableRow>
<TableCell>
<TableCellLink href={`/recipient/${recipient.id}`} title={recipient.email}>
{#if recipient.email}
<a href={`/recipient/${recipient.id}`}>
{recipient.email}
</a>
{recipient.email}
{/if}
</TableCell>
</TableCellLink>
<TableCell>
{#if recipient.firstName}
<button
on:click={() => {
openUpdateModal(recipient.id);
}}
class="block w-full py-1 text-left"
>
{recipient.firstName}
</button>
@@ -430,6 +430,7 @@
on:click={() => {
openUpdateModal(recipient.id);
}}
class="block w-full py-1 text-left"
>
{recipient.lastName}
</button>
@@ -8,6 +8,7 @@
import TextField from '$lib/components/TextField.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import TableUpdateButton from '$lib/components/table/TableUpdateButton.svelte';
import TableDeleteButton from '$lib/components/table/TableDeleteButton2.svelte';
import { addToast } from '$lib/store/toast';
@@ -232,17 +233,13 @@
>
{#each groups as group}
<TableRow>
<TableCell>
<a href={`/recipient/group/${group.id}`}>
{group.name}
</a>
</TableCell>
<TableCellLink href={`/recipient/group/${group.id}`} title={group.name}>
{group.name}
</TableCellLink>
<TableCell>
<a href={`/recipient/group/${group.id}`}>
{group.recipientCount}
</a>
</TableCell>
<TableCellLink href={`/recipient/group/${group.id}`} title={group.recipientCount}>
{group.recipientCount}
</TableCellLink>
<TableCellEmpty />
<TableCellAction>
<TableDropDownEllipsis>
@@ -9,6 +9,7 @@
import SubHeadline from '$lib/components/SubHeadline.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import { addToast } from '$lib/store/toast';
import FormError from '$lib/components/FormError.svelte';
import TableCellEmpty from '$lib/components/table/TableCellEmpty.svelte';
@@ -391,16 +392,16 @@
{#each recipients as recipient}
<TableRow>
<TableCell value={recipient.email} />
<TableCell>
<a href="/recipient/{recipient.id}">{recipient.firstName}</a>
</TableCell>
<TableCell>
<a href="/recipient/{recipient.id}">{recipient.lastName}</a>
</TableCell>
<TableCellLink href="/recipient/{recipient.id}" title={recipient.firstName}>
{recipient.firstName}
</TableCellLink>
<TableCellLink href="/recipient/{recipient.id}" title={recipient.lastName}>
{recipient.lastName}
</TableCellLink>
<TableCell value={recipient.phone} />
<TableCell>
<a href="/recipient/{recipient.id}">{recipient.extraIdentifier}</a>
</TableCell>
<TableCellLink href="/recipient/{recipient.id}" title={recipient.extraIdentifier}>
{recipient.extraIdentifier}
</TableCellLink>
<TableCell value={recipient.position} />
<TableCell value={recipient.department} />
<TableCell value={recipient.city} />
@@ -441,6 +441,7 @@
}}
{...globalButtonDisabledAttributes(conf, contextCompanyID)}
title={conf.name}
class="block w-full py-1 text-left"
>
{conf.name}
</button>
+1
View File
@@ -263,6 +263,7 @@
on:click={() => {
showEditModal(user.id);
}}
class="block w-full py-1 text-left"
>
{user.username}
</button></TableCell