subscription_model -> subscription

This commit is contained in:
Alex
2025-03-24 18:00:57 +01:00
parent 2af4575ff2
commit 87f08dd3be
12 changed files with 145 additions and 163 deletions

View File

@@ -17,7 +17,7 @@ interface Membership {
start_date: string | '';
end_date: string | '';
parent_member_id: number | -1;
subscription_model: Subscription;
subscription: Subscription;
}
interface BankAccount {

View File

@@ -11,12 +11,14 @@
/** @type {import('../../routes/auth/about/[id]/$types').ActionData} */
export let form;
/** @type {App.Locals['subscriptions']}*/
/** @type {App.Locals['subscriptions'] | null}*/
export let subscriptions;
/** @type {App.Locals['user']} */
export let user;
export let submit_form = true;
// Ensure licence is initialized before passing to child
$: if (user && !user.licence) {
user.licence = defaultLicence();
@@ -30,7 +32,7 @@
// $: isNewUser = user === null;
$: isLoading = user === undefined;
/** @type {App.Locals['licence_categories']} */
/** @type {App.Locals['licence_categories'] | null} */
export let licence_categories;
const userStatusOptions = [
@@ -60,9 +62,13 @@
];
const dispatch = createEventDispatcher();
const TABS = hasPrivilige(user, PERMISSIONS.Member)
? ['profile', 'licence', 'membership', 'bankaccount']
: ['profile', 'bankaccount', 'membership'];
const TABS = [
'profile',
'bankaccount',
...(hasPrivilige(user, PERMISSIONS.Member) ? 'membership' : []),
...(user.licence ? 'licence' : [])
];
let activeTab = TABS[0];
let isUpdating = false,
@@ -70,14 +76,16 @@
confirm_password = '';
/** @type {Object.<string, App.Locals['licence_categories']>} */
$: groupedCategories = groupCategories(licence_categories);
$: subscriptionModelOptions = subscriptions.map((sub) => ({
value: sub?.name ?? '',
label: sub?.name ?? ''
}));
$: selectedSubscriptionModel =
subscriptions.find((sub) => sub?.name === user.membership?.subscription_model.name) || null;
$: groupedCategories = licence_categories ? groupCategories(licence_categories) : {};
$: subscriptionModelOptions = subscriptions
? subscriptions.map((sub) => ({
value: sub?.name ?? '',
label: sub?.name ?? ''
}))
: [];
$: selectedSubscriptionModel = subscriptions
? subscriptions.find((sub) => sub?.name === user.membership?.subscription.name) || null
: null;
/**
* creates groups of categories depending on the first letter
* @param {App.Locals['licence_categories']} categories - the categories to sort and group
@@ -99,17 +107,25 @@
);
}
/** @type {import('../../routes/auth/about/[id]/$types').SubmitFunction} */
const handleUpdate = async () => {
/** @type {import('@sveltejs/kit').SubmitFunction} */
const handleUpdate = ({ cancel }) => {
if (!submit_form) {
cancel();
dispatch('close');
return;
}
isUpdating = true;
return async ({ result }) => {
isUpdating = false;
if (result.type === 'success' || result.type === 'redirect') {
dispatch('close');
} else {
document.querySelector('.modal .container')?.scrollTo({ top: 0, behavior: 'smooth' });
}
await applyAction(result);
console.log('submitting');
return submit_form ? await applyAction(result) : undefined;
};
};
</script>
@@ -117,7 +133,18 @@
{#if isLoading}
<SmallLoader width={30} message={$t('loading.user_data')} />
{:else if user}
<form class="content" action="?/updateUser" method="POST" use:enhance={handleUpdate}>
<form
class="content"
action="?/updateUser"
method="POST"
use:enhance={handleUpdate}
on:submit={(/** @type{SubmitEvent}*/ e) => {
if (!submit_form) {
e.preventDefault();
dispatch('close');
}
}}
>
<input name="user[id]" type="hidden" bind:value={user.id} />
<h1 class="step-title" style="text-align: center;">
{user.id ? $t('user.edit') : $t('user.create')}
@@ -229,16 +256,14 @@
bind:value={user.phone}
placeholder={$t('placeholder.phone')}
/>
{#if hasPrivilige(user, PERMISSIONS.Member)}
<InputField
name="user[dateofbirth]"
type="date"
label={$t('user.dateofbirth')}
bind:value={user.dateofbirth}
placeholder={$t('placeholder.dateofbirth')}
readonly={readonlyUser}
/>
{/if}
<InputField
name="user[dateofbirth]"
type="date"
label={$t('user.dateofbirth')}
bind:value={user.dateofbirth}
placeholder={$t('placeholder.dateofbirth')}
readonly={readonlyUser}
/>
<InputField
name="user[address]"
label={$t('address')}
@@ -271,7 +296,7 @@
{/if}
</div>
{#if hasPrivilige(user, PERMISSIONS.Member)}
{#if hasPrivilige(user, PERMISSIONS.Member) && user.licence}
<div class="tab-content" style="display: {activeTab === 'licence' ? 'block' : 'none'}">
<InputField
name="user[licence][status]"
@@ -342,7 +367,10 @@
</div>
</div>
{/if}
<div class="tab-content" style="display: {activeTab === 'membership' ? 'block' : 'none'}">
<div
class="tab-content"
style="display: {activeTab === 'membership' && subscriptions ? 'block' : 'none'}"
>
<InputField
name="user[membership][status]"
type="select"
@@ -352,10 +380,10 @@
readonly={readonlyUser}
/>
<InputField
name="user[membership][subscription_model][name]"
name="user[membership][subscription][name]"
type="select"
label={$t('subscription.subscription')}
bind:value={user.membership.subscription_model.name}
label={$t('subscriptions.subscription')}
bind:value={user.membership.subscription.name}
options={subscriptionModelOptions}
readonly={readonlyUser || !hasPrivilige(user, PERMISSIONS.Member)}
/>
@@ -363,22 +391,22 @@
{#if hasPrivilige(user, PERMISSIONS.Member)}
<div class="subscription-column">
<p>
<strong>{$t('subscription.monthly_fee')}:</strong>
<strong>{$t('subscriptions.monthly_fee')}:</strong>
{selectedSubscriptionModel?.monthly_fee || '-'}
</p>
<p>
<strong>{$t('subscription.hourly_rate')}:</strong>
<strong>{$t('subscriptions.hourly_rate')}:</strong>
{selectedSubscriptionModel?.hourly_rate || '-'}
</p>
{#if selectedSubscriptionModel?.included_hours_per_year}
<p>
<strong>{$t('subscription.included_hours_per_year')}:</strong>
<strong>{$t('subscriptions.included_hours_per_year')}:</strong>
{selectedSubscriptionModel?.included_hours_per_year}
</p>
{/if}
{#if selectedSubscriptionModel?.included_hours_per_month}
<p>
<strong>{$t('subscription.included_hours_per_month')}:</strong>
<strong>{$t('subscriptions.included_hours_per_month')}:</strong>
{selectedSubscriptionModel?.included_hours_per_month}
</p>
{/if}
@@ -391,7 +419,7 @@
</p>
{#if selectedSubscriptionModel?.conditions}
<p>
<strong>{$t('subscription.conditions')}:</strong>
<strong>{$t('subscriptions.conditions')}:</strong>
{selectedSubscriptionModel?.conditions}
</p>
{/if}

View File

@@ -7,11 +7,12 @@ export default {
5: 'Passiv'
},
userRole: {
'-5': 'Unfallgegner',
0: 'Sponsor',
1: 'Mitglied',
2: 'Betrachter',
4: 'Bearbeiter',
8: 'Adm/endinistrator'
8: 'Administrator'
},
placeholder: {
car_name: 'Hat das Fahrzeug einen Namen?',
@@ -75,7 +76,7 @@ export default {
validation: {
invalid: 'ungültig',
invalid_user_id: 'Nutzer ID ungültig',
invalid_subscription_model: 'Model nicht gefunden',
invalid_subscription: 'Model nicht gefunden',
user_not_found: '{field} konnte nicht gefunden werden',
invalid_user_data: 'Nutzerdaten ungültig',
user_not_found_or_wrong_password: 'Existiert nicht oder falsches Passwort',
@@ -141,7 +142,7 @@ export default {
role: 'Nutzerrolle',
supporter: 'Sponsor'
},
subscription: {
subscriptions: {
name: 'Modellname',
edit: 'Modell bearbeiten',
create: 'Modell erstellen',
@@ -176,6 +177,8 @@ export default {
user_deletion: 'Soll der Nutzer {firstname} {lastname} wirklich gelöscht werden?',
subscription_deletion: 'Soll das Tarifmodell {name} wirklich gelöscht werden?',
car_deletion: 'Soll das Fahrzeug {name} wirklich gelöscht werden?',
insurance_deletion: 'Soll die Versicherung {name} wirklich gelöscht werden?',
damage_deletion: 'Soll der Schaden {name} wirklich gelöscht werden?',
backend_access: 'Soll {firstname} {lastname} Backend Zugriff gewährt werden?'
},
cancel: 'Abbrechen',
@@ -192,7 +195,7 @@ export default {
supporter: 'Sponsoren',
mandate_date_signed: 'Mandatserteilungsdatum',
licence_categories: 'Führerscheinklassen',
subscription_model: 'Mitgliedschatfsmodell',
subscription: 'Mitgliedschatfsmodell',
licence: 'Führerschein',
licence_number: 'Führerscheinnummer',
insurance: 'Versicherung',

View File

@@ -69,7 +69,7 @@ export default {
validation: {
invalid: 'Invalid',
invalid_user_id: 'Invalid user ID',
invalid_subscription_model: 'Model not found',
invalid_subscription: 'Model not found',
user_not_found: '{field} could not be found',
invalid_user_data: 'Invalid user data',
user_not_found_or_wrong_password: 'Does not exist or wrong password',
@@ -128,7 +128,7 @@ export default {
role: 'User Role',
supporter: 'Sponsor'
},
subscription: {
subscriptions: {
name: 'Model Name',
edit: 'Edit Model',
create: 'Create Model',
@@ -160,7 +160,7 @@ export default {
supporter: 'Sponsors',
mandate_date_signed: 'Mandate Signing Date',
licence_categories: 'Drivers licence Categories',
subscription_model: 'Membership Model',
subscription: 'Membership Model',
licence: 'Drivers licence',
licence_number: 'Drivers licence Number',
issued_date: 'Issue Date',

View File

@@ -11,4 +11,5 @@ export const PERMISSIONS = {
Super: 8
};
export const SUPPORTER_SUBSCRIPTION_MODEL_NAME = 'Keins';
export const SUPPORTER_SUBSCRIPTION_NAME = 'Keins';
export const OPPONENT_SUBSCRIPTION_NAME = 'Keins';

View File

@@ -1,6 +1,6 @@
// src/lib/utils/defaults.js
import { SUPPORTER_SUBSCRIPTION_MODEL_NAME } from './constants';
import { OPPONENT_SUBSCRIPTION_NAME, SUPPORTER_SUBSCRIPTION_NAME } from './constants';
/**
* @returns {App.Types['subscription']}
@@ -28,7 +28,7 @@ export function defaultMembership() {
start_date: '',
end_date: '',
parent_member_id: 0,
subscription_model: defaultSubscription()
subscription: defaultSubscription()
};
}
@@ -93,31 +93,25 @@ export function defaultUser() {
* @returns {App.Locals['user']}
*/
export function defaultSupporter() {
let supporter = {
id: 0,
email: '',
first_name: '',
last_name: '',
password: '',
phone: '',
address: '',
zip_code: '',
city: '',
company: '',
dateofbirth: '',
notes: '',
profile_picture: '',
payment_status: 0,
status: 5,
role_id: 0,
membership: defaultMembership(),
licence: defaultLicence(),
bank_account: defaultBankAccount()
};
supporter.membership.subscription_model.name = SUPPORTER_SUBSCRIPTION_MODEL_NAME;
let supporter = defaultUser();
supporter.status = 5;
supporter.role_id = 0;
supporter.licence = null;
supporter.membership.subscription.name = SUPPORTER_SUBSCRIPTION_NAME;
return supporter;
}
/**
* @returns {App.Locals['user']}
*/
export function defaultOpponent() {
let opponent = defaultUser();
opponent.status = 5;
opponent.role_id = -1;
opponent.licence = null;
opponent.membership.subscription.name = OPPONENT_SUBSCRIPTION_NAME;
return opponent;
}
/**
* @returns {App.Types['location']}
*/

View File

@@ -62,44 +62,16 @@ export function formDataToObject(formData) {
* @param {{ object: Partial<App.Locals['user']>, confirm_password: string} } rawData - The raw form data object
* @returns {{ user: Partial<App.Locals['user']> }} Processed user data
*/
export function processUserFormData(rawData) {
/** @type {{ user: Partial<App.Locals['user']> }} */
let processedData = {
user: {
id: Number(rawData.object.id) || 0,
status: Number(rawData.object.status),
role_id: Number(rawData.object.role_id),
first_name: String(rawData.object.first_name),
last_name: String(rawData.object.last_name),
email: String(rawData.object.email),
phone: String(rawData.object.phone || ''),
company: String(rawData.object.company || ''),
dateofbirth: toRFC3339(String(rawData.object.dateofbirth || '')),
address: String(rawData.object.address || ''),
zip_code: String(rawData.object.zip_code || ''),
city: String(rawData.object.city || ''),
notes: String(rawData.object.notes || ''),
profile_picture: String(rawData.object.profile_picture || ''),
membership: {
id: Number(rawData.object.membership?.id) || 0,
status: Number(rawData.object.membership?.status),
start_date: toRFC3339(String(rawData.object.membership?.start_date || '')),
end_date: toRFC3339(String(rawData.object.membership?.end_date || '')),
parent_member_id: Number(rawData.object.membership?.parent_member_id) || 0,
subscription_model: {
id: Number(rawData.object.membership?.subscription_model?.id) || 0,
name: String(rawData.object.membership?.subscription_model?.name) || '',
details: String(rawData.object.membership?.subscription_model?.details) || '',
conditions: String(rawData.object.membership?.subscription_model?.conditions) || '',
hourly_rate: Number(rawData.object.membership?.subscription_model?.hourly_rate) || 0,
monthly_fee: Number(rawData.object.membership?.subscription_model?.monthly_fee) || 0,
included_hours_per_month:
Number(rawData.object.membership?.subscription_model?.included_hours_per_month) || 0,
included_hours_per_year:
Number(rawData.object.membership?.subscription_model?.included_hours_per_year) || 0
}
},
export function processMembershipFormData(membership) {
return {
id: Number(membership.id) || 0,
status: Number(membership.status),
start_date: toRFC3339(String(membership.start_date || '')),
end_date: toRFC3339(String(membership.end_date || '')),
parent_member_id: Number(membership.parent_member_id) || 0,
subscription: processSubscriptionFormData(membership.subscription)
};
}
licence: {
id: Number(rawData.object.licence?.id) || 0,
@@ -143,22 +115,20 @@ export function processUserFormData(rawData) {
/**
* Processes the raw form data into the expected subscription data structure
* @param {{ object: Partial<App.Types['subscription']>, confirm_password: string }} rawData - The raw form data object
* @returns {{ subscription: Partial<App.Types['subscription']> }} Processed user data
* @param {Partial<App.Types['subscription']>} subscription - The raw form data object
* @returns {App.Types['subscription']} Processed user data
*/
export function processSubscriptionFormData(rawData) {
/** @type {{ subscription: Partial<App.Types['subscription']> }} */
export function processSubscriptionFormData(subscription) {
/** @type {Partial<App.Types['subscription']>} */
let processedData = {
subscription: {
id: Number(rawData.object.id) || 0,
name: String(rawData.object.name) || '',
details: String(rawData.object.details) || '',
conditions: String(rawData.object.conditions) || '',
hourly_rate: Number(rawData.object.hourly_rate) || 0,
monthly_fee: Number(rawData.object.monthly_fee) || 0,
included_hours_per_month: Number(rawData.object.included_hours_per_month) || 0,
included_hours_per_year: Number(rawData.object.included_hours_per_year) || 0
}
id: Number(subscription.id) || 0,
name: String(subscription.name) || '',
details: String(subscription.details) || '',
conditions: String(subscription.conditions) || '',
hourly_rate: Number(subscription.hourly_rate) || 0,
monthly_fee: Number(subscription.monthly_fee) || 0,
included_hours_per_month: Number(subscription.included_hours_per_month) || 0,
included_hours_per_year: Number(subscription.included_hours_per_year) || 0
};
const clean = JSON.parse(JSON.stringify(processedData), (key, value) =>
value !== null && value !== '' ? value : undefined

View File

@@ -87,10 +87,11 @@ export const actions = {
updateSubscription: async ({ request, fetch, cookies }) => {
let formData = await request.formData();
const rawData = formDataToObject(formData);
const processedData = processSubscriptionFormData(rawData);
const rawFormData = formDataToObject(formData);
const rawSubscription = /** @type {Partial<App.Types['subscription']>} */ (rawFormData.object);
const subscription = processSubscriptionFormData(rawSubscription);
const isCreating = !processedData.subscription.id || processedData.subscription.id === 0;
const isCreating = !subscription.id || subscription.id === 0;
console.log('Is creating: ', isCreating);
const apiURL = `${BASE_API_URI}/auth/subscriptions`;
@@ -102,7 +103,7 @@ export const actions = {
'Content-Type': 'application/json',
Cookie: `jwt=${cookies.get('jwt')}`
},
body: JSON.stringify(processedData.subscription)
body: JSON.stringify(subscription)
};
const res = await fetch(apiURL, requestOptions);

View File

@@ -83,9 +83,7 @@
user.licence?.number?.toLowerCase()
].some((field) => field?.includes(term));
const subscriptionMatch = user.membership?.subscription_model?.name
?.toLowerCase()
.includes(term);
const subscriptionMatch = user.membership?.subscription?.name?.toLowerCase().includes(term);
const licenceCategoryMatch = user.licence?.categories?.some((cat) =>
cat.category.toLowerCase().includes(term)
@@ -277,8 +275,8 @@
<td>{user.email}</td>
</tr>
<tr>
<th>{$t('subscription.subscription')}</th>
<td>{user.membership?.subscription_model?.name}</td>
<th>{$t('subscriptions.subscription')}</th>
<td>{user.membership?.subscription?.name}</td>
</tr>
<tr>
<th>{$t('status')}</th>
@@ -462,7 +460,7 @@
<span class="nav-badge"
>{members.filter(
(/** @type{App.Locals['user']}*/ user) =>
user.membership?.subscription_model?.name === subscription.name
user.membership?.subscription?.name === subscription.name
).length}</span
>
</summary>
@@ -514,33 +512,20 @@
<i class="fas fa-edit"></i>
{$t('edit')}
</button>
{#if !members.some(/** @param{App.Locals['user']} user */ (user) => user.membership?.subscription_model?.id === subscription.id)}
<form
method="POST"
action="?/subscriptionDelete"
use:enhance={() => {
return async ({ result }) => {
if (result.type === 'success' || result.type === 'redirect') {
await applyAction(result);
} else {
document
.querySelector('.accordion-content')
?.scrollTo({ top: 0, behavior: 'smooth' });
await applyAction(result);
}
};
}}
on:submit|preventDefault={(/** @type {SubmitEvent} */ e) => {
if (
!confirm(
$t('dialog.subscription_deletion', {
values: {
name: subscription.name || ''
}
})
)
) {
e.preventDefault(); // Cancel form submission if user declines
{/if}
{#if !members.some(/** @param{App.Locals['user']} user */ (user) => user.membership?.subscription?.id === subscription.id)}
<form
method="POST"
action="?/subscriptionDelete"
use:enhance={() => {
return async ({ result }) => {
if (result.type === 'success' || result.type === 'redirect') {
await applyAction(result);
} else {
document
.querySelector('.accordion-content')
?.scrollTo({ top: 0, behavior: 'smooth' });
await applyAction(result);
}
}}
>