frontend: add car handling
This commit is contained in:
44
frontend/src/app.d.ts
vendored
44
frontend/src/app.d.ts
vendored
@@ -68,12 +68,52 @@ interface User {
|
|||||||
notes: string | '';
|
notes: string | '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Car {
|
||||||
|
id: number | -1;
|
||||||
|
name: string | '';
|
||||||
|
status: number | 0;
|
||||||
|
brand: string | '';
|
||||||
|
model: string | '';
|
||||||
|
price: number | 0;
|
||||||
|
rate: number | 0;
|
||||||
|
start_date: string | '';
|
||||||
|
end_date: string | '';
|
||||||
|
color: string | '';
|
||||||
|
licence_plate: string | '';
|
||||||
|
location: Location;
|
||||||
|
damages: Damage[] | [];
|
||||||
|
insurances: Insurance[] | [];
|
||||||
|
notes: string | '';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Location {
|
||||||
|
latitude: number | 0;
|
||||||
|
longitude: number | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Damage {
|
||||||
|
id: number | -1;
|
||||||
|
opponent: User;
|
||||||
|
insurance: Insurance | null;
|
||||||
|
notes: string | '';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Insurance {
|
||||||
|
id: number | -1;
|
||||||
|
company: string | '';
|
||||||
|
reference: string | '';
|
||||||
|
start_date: string | '';
|
||||||
|
end_date: string | '';
|
||||||
|
notes: string | '';
|
||||||
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
// interface Error {}
|
// interface Error {}
|
||||||
interface Locals {
|
interface Locals {
|
||||||
user: User;
|
user: User;
|
||||||
users: User[];
|
users: User[];
|
||||||
|
cars: Cars[];
|
||||||
subscriptions: Subscription[];
|
subscriptions: Subscription[];
|
||||||
licence_categories: LicenceCategory[];
|
licence_categories: LicenceCategory[];
|
||||||
}
|
}
|
||||||
@@ -84,6 +124,10 @@ declare global {
|
|||||||
licence: Licence;
|
licence: Licence;
|
||||||
licenceCategory: LicenceCategory;
|
licenceCategory: LicenceCategory;
|
||||||
bankAccount: BankAccount;
|
bankAccount: BankAccount;
|
||||||
|
car: Car;
|
||||||
|
insurance: Insurance;
|
||||||
|
location: Location;
|
||||||
|
damage: Damage;
|
||||||
}
|
}
|
||||||
// interface PageData {}
|
// interface PageData {}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
export async function load({ data }) {
|
export async function load({ data }) {
|
||||||
return {
|
return {
|
||||||
users: data.users,
|
users: data.users,
|
||||||
user: data.user
|
user: data.user,
|
||||||
|
cars: data.cars
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,63 @@
|
|||||||
import { BASE_API_URI } from '$lib/utils/constants';
|
import { BASE_API_URI } from '$lib/utils/constants';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import { userDatesFromRFC3339, refreshCookie } from '$lib/utils/helpers';
|
import { userDatesFromRFC3339, refreshCookie, carDatesFromRFC3339 } from '$lib/utils/helpers';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
/** @type {import('./$types').LayoutServerLoad} */
|
/** @type {import('./$types').LayoutServerLoad} */
|
||||||
export async function load({ cookies, fetch, locals }) {
|
export async function load({ cookies, fetch, locals }) {
|
||||||
const jwt = cookies.get('jwt');
|
const jwt = cookies.get('jwt');
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${BASE_API_URI}/auth/users/`, {
|
const [usersResponse, carsResponse] = await Promise.all([
|
||||||
credentials: 'include',
|
fetch(`${BASE_API_URI}/auth/users`, {
|
||||||
headers: {
|
credentials: 'include',
|
||||||
Cookie: `jwt=${jwt}`
|
headers: { Cookie: `jwt=${jwt}` }
|
||||||
}
|
}),
|
||||||
});
|
fetch(`${BASE_API_URI}/auth/cars`, {
|
||||||
if (!response.ok) {
|
credentials: 'include',
|
||||||
// Clear the invalid JWT cookie
|
headers: { Cookie: `jwt=${jwt}` }
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
if (!usersResponse.ok || !carsResponse.ok) {
|
||||||
cookies.delete('jwt', { path: '/' });
|
cookies.delete('jwt', { path: '/' });
|
||||||
throw redirect(302, `${base}/auth/login?next=${base}/auth/admin/users/`);
|
throw redirect(302, `${base}/auth/login?next=${base}/auth/admin/users/`);
|
||||||
}
|
}
|
||||||
|
const [usersData, carsData] = await Promise.all([usersResponse.json(), carsResponse.json()]);
|
||||||
|
// const response = await fetch(`${BASE_API_URI}/auth/users/`, {
|
||||||
|
// credentials: 'include',
|
||||||
|
// headers: {
|
||||||
|
// Cookie: `jwt=${jwt}`
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// if (!response.ok) {
|
||||||
|
// // Clear the invalid JWT cookie
|
||||||
|
// cookies.delete('jwt', { path: '/' });
|
||||||
|
// throw redirect(302, `${base}/auth/login?next=${base}/auth/admin/users/`);
|
||||||
|
// }
|
||||||
|
|
||||||
const data = await response.json();
|
// const data = await response.json();
|
||||||
/** @type {App.Locals['users']}*/
|
/** @type {App.Locals['users']}*/
|
||||||
const users = data.users;
|
const users = usersData.users;
|
||||||
|
/** @type {App.Types['car'][]} */
|
||||||
|
const cars = carsData.cars;
|
||||||
|
|
||||||
users.forEach((user) => {
|
users.forEach((user) => {
|
||||||
userDatesFromRFC3339(user);
|
userDatesFromRFC3339(user);
|
||||||
});
|
});
|
||||||
|
cars.forEach((car) => {
|
||||||
|
carDatesFromRFC3339(car);
|
||||||
|
});
|
||||||
|
|
||||||
locals.users = users;
|
locals.users = users;
|
||||||
|
locals.cars = cars;
|
||||||
// Check if the server sent a new token
|
// Check if the server sent a new token
|
||||||
const newToken = response.headers.get('Set-Cookie');
|
const newToken = usersResponse.headers.get('Set-Cookie');
|
||||||
refreshCookie(newToken, cookies);
|
refreshCookie(newToken, cookies);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscriptions: locals.subscriptions,
|
subscriptions: locals.subscriptions,
|
||||||
licence_categories: locals.licence_categories,
|
licence_categories: locals.licence_categories,
|
||||||
users: locals.users,
|
users: locals.users,
|
||||||
user: locals.user
|
user: locals.user,
|
||||||
|
cars: locals.cars
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching data:', error);
|
console.error('Error fetching data:', error);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { formatError, hasPrivilige, userDatesFromRFC3339 } from '$lib/utils/help
|
|||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import {
|
import {
|
||||||
formDataToObject,
|
formDataToObject,
|
||||||
|
processCarFormData,
|
||||||
processSubscriptionFormData,
|
processSubscriptionFormData,
|
||||||
processUserFormData
|
processUserFormData
|
||||||
} from '$lib/utils/processing';
|
} from '$lib/utils/processing';
|
||||||
@@ -36,7 +37,12 @@ export const actions = {
|
|||||||
updateUser: async ({ request, fetch, cookies, locals }) => {
|
updateUser: async ({ request, fetch, cookies, locals }) => {
|
||||||
let formData = await request.formData();
|
let formData = await request.formData();
|
||||||
|
|
||||||
const rawData = formDataToObject(formData);
|
const rawFormData = formDataToObject(formData);
|
||||||
|
/** @type {{object: Partial<App.Locals['user']>, confirm_password: string}} */
|
||||||
|
const rawData = {
|
||||||
|
object: /** @type {Partial<App.Locals['user']>} */ (rawFormData.object),
|
||||||
|
confirm_password: rawFormData.confirm_password
|
||||||
|
};
|
||||||
const processedData = processUserFormData(rawData);
|
const processedData = processUserFormData(rawData);
|
||||||
|
|
||||||
console.dir(processedData.user.membership);
|
console.dir(processedData.user.membership);
|
||||||
@@ -96,7 +102,55 @@ export const actions = {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Cookie: `jwt=${cookies.get('jwt')}`
|
Cookie: `jwt=${cookies.get('jwt')}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify(processedData)
|
body: JSON.stringify(processedData.subscription)
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fetch(apiURL, requestOptions);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const response = await res.json();
|
||||||
|
const errors = formatError(response.errors);
|
||||||
|
return fail(400, { errors: errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await res.json();
|
||||||
|
console.log('Server success response:', response);
|
||||||
|
throw redirect(303, `${base}/auth/admin/users`);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param request - The request object
|
||||||
|
* @param fetch - Fetch object from sveltekit
|
||||||
|
* @param cookies - SvelteKit's cookie object
|
||||||
|
* @returns Error data or redirects user to the home page or the previous page
|
||||||
|
*/
|
||||||
|
updateCar: async ({ request, fetch, cookies }) => {
|
||||||
|
let formData = await request.formData();
|
||||||
|
|
||||||
|
const rawFormData = formDataToObject(formData);
|
||||||
|
/** @type {{object: Partial<App.Types['car']>, confirm_password: string}} */
|
||||||
|
const rawData = {
|
||||||
|
object: /** @type {Partial<App.Types['car']>} */ (rawFormData.object),
|
||||||
|
confirm_password: rawFormData.confirm_password
|
||||||
|
};
|
||||||
|
const processedData = processCarFormData(rawData);
|
||||||
|
|
||||||
|
const isCreating = !processedData.car.id || processedData.car.id === 0;
|
||||||
|
console.log('Is creating: ', isCreating);
|
||||||
|
console.log('sending: ', JSON.stringify(processedData.car));
|
||||||
|
|
||||||
|
const apiURL = `${BASE_API_URI}/auth/cars`;
|
||||||
|
|
||||||
|
/** @type {RequestInit} */
|
||||||
|
const requestOptions = {
|
||||||
|
method: isCreating ? 'POST' : 'PUT',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Cookie: `jwt=${cookies.get('jwt')}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(processedData.car)
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch(apiURL, requestOptions);
|
const res = await fetch(apiURL, requestOptions);
|
||||||
@@ -123,7 +177,12 @@ export const actions = {
|
|||||||
userDelete: async ({ request, fetch, cookies }) => {
|
userDelete: async ({ request, fetch, cookies }) => {
|
||||||
let formData = await request.formData();
|
let formData = await request.formData();
|
||||||
|
|
||||||
const rawData = formDataToObject(formData);
|
const rawFormData = formDataToObject(formData);
|
||||||
|
/** @type {{object: Partial<App.Locals['user']>, confirm_password: string}} */
|
||||||
|
const rawData = {
|
||||||
|
object: /** @type {Partial<App.Locals['user']>} */ (rawFormData.object),
|
||||||
|
confirm_password: rawFormData.confirm_password
|
||||||
|
};
|
||||||
const processedData = processUserFormData(rawData);
|
const processedData = processUserFormData(rawData);
|
||||||
|
|
||||||
const apiURL = `${BASE_API_URI}/auth/users`;
|
const apiURL = `${BASE_API_URI}/auth/users`;
|
||||||
@@ -176,7 +235,7 @@ export const actions = {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Cookie: `jwt=${cookies.get('jwt')}`
|
Cookie: `jwt=${cookies.get('jwt')}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify(processedData)
|
body: JSON.stringify(processedData.subscription)
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch(apiURL, requestOptions);
|
const res = await fetch(apiURL, requestOptions);
|
||||||
@@ -191,10 +250,23 @@ export const actions = {
|
|||||||
console.log('Server success response:', response);
|
console.log('Server success response:', response);
|
||||||
throw redirect(303, `${base}/auth/admin/users`);
|
throw redirect(303, `${base}/auth/admin/users`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param request - The request object
|
||||||
|
* @param fetch - Fetch object from sveltekit
|
||||||
|
* @param cookies - SvelteKit's cookie object
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
grantBackendAccess: async ({ request, fetch, cookies }) => {
|
grantBackendAccess: async ({ request, fetch, cookies }) => {
|
||||||
let formData = await request.formData();
|
let formData = await request.formData();
|
||||||
|
|
||||||
const rawData = formDataToObject(formData);
|
const rawFormData = formDataToObject(formData);
|
||||||
|
/** @type {{object: Partial<App.Types['subscription']>, confirm_password: string}} */
|
||||||
|
const rawData = {
|
||||||
|
object: /** @type {Partial<App.Types['subscription']>} */ (rawFormData.object),
|
||||||
|
confirm_password: rawFormData.confirm_password
|
||||||
|
};
|
||||||
const processedData = processUserFormData(rawData);
|
const processedData = processUserFormData(rawData);
|
||||||
console.dir(processedData);
|
console.dir(processedData);
|
||||||
const apiURL = `${BASE_API_URI}/auth/users/activate`;
|
const apiURL = `${BASE_API_URI}/auth/users/activate`;
|
||||||
|
|||||||
@@ -8,7 +8,13 @@
|
|||||||
import { applyAction, enhance } from '$app/forms';
|
import { applyAction, enhance } from '$app/forms';
|
||||||
import { hasPrivilige, receive, send } from '$lib/utils/helpers';
|
import { hasPrivilige, receive, send } from '$lib/utils/helpers';
|
||||||
import { PERMISSIONS } from '$lib/utils/constants';
|
import { PERMISSIONS } from '$lib/utils/constants';
|
||||||
import { defaultSupporter, defaultUser } from '$lib/utils/defaults';
|
import {
|
||||||
|
defaultCar,
|
||||||
|
defaultSubscription,
|
||||||
|
defaultSupporter,
|
||||||
|
defaultUser
|
||||||
|
} from '$lib/utils/defaults';
|
||||||
|
import CarEditForm from '$lib/components/CarEditForm.svelte';
|
||||||
|
|
||||||
/** @type {import('./$types').ActionData} */
|
/** @type {import('./$types').ActionData} */
|
||||||
export let form;
|
export let form;
|
||||||
@@ -16,18 +22,17 @@
|
|||||||
$: ({
|
$: ({
|
||||||
user = [],
|
user = [],
|
||||||
users = [],
|
users = [],
|
||||||
|
cars = [],
|
||||||
licence_categories = [],
|
licence_categories = [],
|
||||||
subscriptions = [],
|
subscriptions = [],
|
||||||
payments = []
|
payments = []
|
||||||
} = $page.data);
|
} = $page.data);
|
||||||
|
|
||||||
let activeSection = 'members';
|
let activeSection = 'members';
|
||||||
/** @type{App.Locals['user'] | null} */
|
|
||||||
let selectedUser = null;
|
/** @type{App.Types['car'] | App.Types['subscription'] | App.Locals['user'] | null} */
|
||||||
/** @type{App.Types['subscription'] | null} */
|
let selected = null;
|
||||||
let selectedSubscription = null;
|
|
||||||
let showSubscriptionModal = false;
|
|
||||||
let showUserModal = false;
|
|
||||||
let searchTerm = '';
|
let searchTerm = '';
|
||||||
|
|
||||||
$: members = users.filter((/** @type{App.Locals['user']} */ user) => {
|
$: members = users.filter((/** @type{App.Locals['user']} */ user) => {
|
||||||
@@ -95,29 +100,8 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the edit modal for the selected user.
|
|
||||||
* @param {App.Locals['user']} user The user to edit.
|
|
||||||
*/
|
|
||||||
const openEditUserModal = (user) => {
|
|
||||||
selectedUser = user;
|
|
||||||
showUserModal = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the edit modal for the selected subscription.
|
|
||||||
* @param {App.Types['subscription'] | null} subscription The user to edit.
|
|
||||||
*/
|
|
||||||
const openEditSubscriptionModal = (subscription) => {
|
|
||||||
selectedSubscription = subscription;
|
|
||||||
showSubscriptionModal = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
showUserModal = false;
|
selected = null;
|
||||||
showSubscriptionModal = false;
|
|
||||||
selectedUser = null;
|
|
||||||
selectedSubscription = null;
|
|
||||||
if (form) {
|
if (form) {
|
||||||
form.errors = [];
|
form.errors = [];
|
||||||
}
|
}
|
||||||
@@ -167,6 +151,16 @@
|
|||||||
<span class="nav-badge">{subscriptions.length}</span>
|
<span class="nav-badge">{subscriptions.length}</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="nav-link {activeSection === 'cars' ? 'active' : ''}"
|
||||||
|
on:click={() => setActiveSection('cars')}
|
||||||
|
>
|
||||||
|
<i class="fas fa-car"></i>
|
||||||
|
{$t('cars')}
|
||||||
|
<span class="nav-badge">{cars.length}</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
class="nav-link {activeSection === 'payments' ? 'active' : ''}"
|
class="nav-link {activeSection === 'payments' ? 'active' : ''}"
|
||||||
@@ -214,7 +208,12 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="btn primary" on:click={() => openEditUserModal(defaultUser())}>
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = defaultUser();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{$t('add_new')}
|
{$t('add_new')}
|
||||||
</button>
|
</button>
|
||||||
@@ -288,7 +287,12 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button class="btn primary" on:click={() => openEditUserModal(user)}>
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = user;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
{$t('edit')}
|
{$t('edit')}
|
||||||
</button>
|
</button>
|
||||||
@@ -350,7 +354,12 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="btn primary" on:click={() => openEditUserModal(defaultSupporter())}>
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = defaultSupporter();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{$t('add_new')}
|
{$t('add_new')}
|
||||||
</button>
|
</button>
|
||||||
@@ -384,7 +393,12 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button class="btn primary" on:click={() => openEditUserModal(user)}>
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = user;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
{$t('edit')}
|
{$t('edit')}
|
||||||
</button>
|
</button>
|
||||||
@@ -429,7 +443,12 @@
|
|||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>{$t('subscription.subscriptions')}</h2>
|
<h2>{$t('subscription.subscriptions')}</h2>
|
||||||
{#if hasPrivilige(user, PERMISSIONS.Super)}
|
{#if hasPrivilige(user, PERMISSIONS.Super)}
|
||||||
<button class="btn primary" on:click={() => openEditSubscriptionModal(null)}>
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = defaultSubscription();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{$t('add_new')}
|
{$t('add_new')}
|
||||||
</button>
|
</button>
|
||||||
@@ -488,7 +507,9 @@
|
|||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button
|
<button
|
||||||
class="btn primary"
|
class="btn primary"
|
||||||
on:click={() => openEditSubscriptionModal(subscription)}
|
on:click={() => {
|
||||||
|
selected = subscription;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
{$t('edit')}
|
{$t('edit')}
|
||||||
@@ -537,6 +558,112 @@
|
|||||||
</details>
|
</details>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{:else if activeSection === 'cars'}
|
||||||
|
<div class="section-header">
|
||||||
|
<h2>{$t('cars')}</h2>
|
||||||
|
{#if hasPrivilige(user, PERMISSIONS.Super)}
|
||||||
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = defaultCar();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
{$t('add_new')}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="accordion">
|
||||||
|
{#each cars as car}
|
||||||
|
<details class="accordion-item">
|
||||||
|
<summary class="accordion-header">
|
||||||
|
{car.model + ' (' + car.licence_plate + ')'}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>{$t('car.model')}</th>
|
||||||
|
<td>{car.brand + ' ' + car.model + ' (' + car.color + ')'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{$t('price')}</th>
|
||||||
|
<td
|
||||||
|
>{car.price + '€'}{car.rate
|
||||||
|
? ' + ' + car.rate + '€/' + $t('month')
|
||||||
|
: ''}</td
|
||||||
|
>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{$t('car.damages')}</th>
|
||||||
|
<td>{car.damages?.length || 0}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{$t('insurance')}</th>
|
||||||
|
<td
|
||||||
|
>{car.insurance
|
||||||
|
? car.insurance.company + '(' + car.insurance.reference + ')'
|
||||||
|
: '-'}</td
|
||||||
|
>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{$t('car.end_date')}</th>
|
||||||
|
<td>{car.end_date || '-'}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{#if hasPrivilige(user, PERMISSIONS.Update)}
|
||||||
|
<div class="button-group">
|
||||||
|
<button
|
||||||
|
class="btn primary"
|
||||||
|
on:click={() => {
|
||||||
|
selected = car;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
{$t('edit')}
|
||||||
|
</button>
|
||||||
|
<form
|
||||||
|
method="POST"
|
||||||
|
action="?/carDelete"
|
||||||
|
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.car_deletion', {
|
||||||
|
values: {
|
||||||
|
name: car.brand + ' ' + car.model + ' (' + car.licence_plate + ')'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
e.preventDefault(); // Cancel form submission if user declines
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input type="hidden" name="subscription[id]" value={car.id} />
|
||||||
|
<button class="btn danger" type="submit">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
{$t('delete')}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
{:else if activeSection === 'payments'}
|
{:else if activeSection === 'payments'}
|
||||||
<h2>{$t('payments')}</h2>
|
<h2>{$t('payments')}</h2>
|
||||||
<div class="accordion">
|
<div class="accordion">
|
||||||
@@ -567,28 +694,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showUserModal && selectedUser !== null}
|
{#if selected && 'email' in selected}
|
||||||
|
// user
|
||||||
<Modal on:close={close}>
|
<Modal on:close={close}>
|
||||||
<UserEditForm
|
<UserEditForm
|
||||||
{form}
|
{form}
|
||||||
editor={user}
|
editor={user}
|
||||||
user={selectedUser}
|
user={selected}
|
||||||
{subscriptions}
|
{subscriptions}
|
||||||
{licence_categories}
|
{licence_categories}
|
||||||
on:cancel={close}
|
on:cancel={close}
|
||||||
on:close={close}
|
on:close={close}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
{:else if showSubscriptionModal}
|
{:else if selected && 'monthly_fee' in selected}
|
||||||
|
//subscription
|
||||||
<Modal on:close={close}>
|
<Modal on:close={close}>
|
||||||
<SubscriptionEditForm
|
<SubscriptionEditForm
|
||||||
{form}
|
{form}
|
||||||
{user}
|
{user}
|
||||||
subscription={selectedSubscription}
|
subscription={selected}
|
||||||
on:cancel={close}
|
on:cancel={close}
|
||||||
on:close={close}
|
on:close={close}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
{:else if selected && 'brand' in selected}
|
||||||
|
<Modal on:close={close}>
|
||||||
|
<CarEditForm {form} editor={user} car={selected} on:cancel={close} on:close={close} />
|
||||||
|
</Modal>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user