squashme
This commit is contained in:
parent
4e4036896e
commit
5c0f557308
@ -1,182 +1,167 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { ItemCount, TodoItem, User } from './types';
|
||||
import { getResponseBodyOrError } from '$lib/api/utils';
|
||||
import type { ItemCount, TodoItem, Token, User } from './types';
|
||||
import { Endpoint } from './types';
|
||||
import type { StringMapping } from '$lib/utils/types';
|
||||
import { getTokenFromLocalstorage } from '$lib/auth/session';
|
||||
|
||||
/**
|
||||
* Retrieves the currently logged in user's JWT from localstorage,
|
||||
* or throws an error if it is not present.
|
||||
* A factory class for creating `Endpoint` instances to interact with the backend API.
|
||||
*
|
||||
* @throws {error} - If the current user is not logged in or if their JWT is not saved in localstorage.
|
||||
* This class provides methods to create specific `Endpoint` instances for different API operations,
|
||||
* such as retrieving user data, retrieving todo items, and more. It follows the factory pattern to
|
||||
* encapsulate the creation of `Endpoint` instances with the necessary configurations and headers
|
||||
* for making API requests.
|
||||
*
|
||||
* The `EndpointFactory` constructor accepts an optional `fetchFunction` parameter, which is the
|
||||
* function used for making API requests. If no `fetchFunction` is provided, the default `fetch`
|
||||
* function is used.
|
||||
*
|
||||
* Example usage:
|
||||
* ```typescript
|
||||
* const endpointFactory = new EndpointFactory();
|
||||
*
|
||||
* const readUserEndpoint = endpointFactory.createReadUserEndpoint(123);
|
||||
* const users = await readUserEndpoint.call();
|
||||
*
|
||||
* const readTodosEndpoint = endpointFactory.createReadTodosEndpoint(123);
|
||||
* const todos = await readTodosEndpoint.call();
|
||||
* ```
|
||||
*/
|
||||
function getTokenOrError(): string {
|
||||
let token = getTokenFromLocalstorage();
|
||||
if (token === null) {
|
||||
export class EndpointFactory {
|
||||
|
||||
/** The function to use for making API requests. */
|
||||
readonly fetchFunction: Function;
|
||||
|
||||
// The JSON web token to send as an authorization bearer token.
|
||||
private _jwt: string | null;
|
||||
|
||||
/**
|
||||
* Constructs a new `EndpointFactory` instance.
|
||||
* @param fetchFunction - The function Endpoints created by this factory use for making API requests. (Default: fetch)
|
||||
*/
|
||||
constructor(fetchFunction: Function = fetch) {
|
||||
this.fetchFunction = fetchFunction;
|
||||
this._jwt = getTokenFromLocalstorage();
|
||||
}
|
||||
|
||||
private _getDefaultHeaders(): StringMapping {
|
||||
return {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
}
|
||||
|
||||
private _getDefaultHeadersWithAuth(): StringMapping {
|
||||
if (this._jwt === null) {
|
||||
throw error(401, 'You are not logged in.');
|
||||
}
|
||||
return token;
|
||||
|
||||
let headers = this._getDefaultHeaders();
|
||||
headers['Authorization'] = `Bearer: ${this._jwt}`;
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the user with the specified ID from the backend API.
|
||||
* Creates an Endpoint which retrieves the user with the specified ID from the backend API.
|
||||
*
|
||||
* @param {number} userId - The ID of the user whom to retrieve.
|
||||
* @param {string} jwt - The JWT appended as a bearer token to authorize the request.
|
||||
* @throws{error} - If the request fails or is not permitted.
|
||||
* @throws {error} If the user is not currently logged in.
|
||||
*/
|
||||
export async function readUser(userId: number, jwt: string = getTokenOrError()): Promise<User> {
|
||||
const endpoint = `/api/users/${userId}`;
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await getResponseBodyOrError(response);
|
||||
return responseJson as User;
|
||||
createReadUserEndpoint(userId: number): Endpoint<User> {
|
||||
return new Endpoint<User>(
|
||||
`/api/users/${userId}`,
|
||||
'GET',
|
||||
this._getDefaultHeadersWithAuth(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of all users from the backend API.
|
||||
* Creates an Endpoint which retrieves the list of all users from the backend API.
|
||||
*
|
||||
* @param {string} jwt - The JWT appended as a bearer token to authorize the request.
|
||||
* @throws{error} - If the request fails or is not permitted.
|
||||
* @throws {error} If the user is not currently logged in.
|
||||
*/
|
||||
export async function readUsers(
|
||||
skip: number,
|
||||
limit: number,
|
||||
sortby: string,
|
||||
sortorder: string,
|
||||
jwt: string = getTokenOrError(),
|
||||
): Promise<User[]> {
|
||||
|
||||
const urlParameters = new URLSearchParams({
|
||||
skip: `${skip}`,
|
||||
limit: `${limit}`,
|
||||
sortby: `${sortby}`,
|
||||
sortorder: `${sortorder}`,
|
||||
});
|
||||
const endpoint = `/api/admin/users/?${urlParameters}`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await getResponseBodyOrError(response);
|
||||
return responseJson as User[];
|
||||
createReadUsersEndpoint(): Endpoint<User[]> {
|
||||
return new Endpoint<User[]>(
|
||||
`/api/admin/users/`,
|
||||
'GET',
|
||||
this._getDefaultHeadersWithAuth(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the total user count from the backend API.
|
||||
* Creates an Endpoint which retrieves the total user count from the backend API.
|
||||
*
|
||||
* @param {string} jwt - The JWT appended as a bearer token to authorize the request.
|
||||
* @throws{error} - If the request fails or is not permitted.
|
||||
* @throws {error} If the user is not currently logged in.
|
||||
*/
|
||||
export async function readUserCount(jwt: string = getTokenOrError()): Promise<number> {
|
||||
const endpoint = '/api/admin/users/total/';
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await getResponseBodyOrError(response);
|
||||
const itemCount = responseJson as ItemCount;
|
||||
return itemCount.total;
|
||||
createReadUserCountEndpoint(): Endpoint<ItemCount> {
|
||||
return new Endpoint<ItemCount>(
|
||||
`/api/admin/users/total/`,
|
||||
'GET',
|
||||
this._getDefaultHeadersWithAuth(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of all todo-items for the user with the specified ID from the backend API.
|
||||
* Creates an Endwpoint which retrieves the list of all todo-items for the user with the specified ID from the backend API.
|
||||
*
|
||||
* @param {string} jwt - The JWT appended as a bearer token to authorize the request.
|
||||
* @throws{error} - If the request fails or is not permitted.
|
||||
* @param {number} userId - The ID of the user whose todo-items to retrieve.
|
||||
* @throws {error} If the user is not currently logged in.
|
||||
*/
|
||||
export async function readTodos(
|
||||
userId: number,
|
||||
skip: number,
|
||||
limit: number,
|
||||
sortby: string,
|
||||
sortorder: string,
|
||||
jwt: string = getTokenOrError(),
|
||||
): Promise<TodoItem[]> {
|
||||
|
||||
const urlParameters = new URLSearchParams({
|
||||
skip: `${skip}`,
|
||||
limit: `${limit}`,
|
||||
sortby: `${sortby}`,
|
||||
sortorder: `${sortorder}`,
|
||||
});
|
||||
const endpoint = `/api/todos/user/${userId}?${urlParameters}`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await getResponseBodyOrError(response);
|
||||
return responseJson as TodoItem[];
|
||||
createReadTodosEndpoint(userId: number): Endpoint<TodoItem[]> {
|
||||
return new Endpoint<TodoItem[]>(
|
||||
`/api/todos/user/${userId}`,
|
||||
'GET',
|
||||
this._getDefaultHeadersWithAuth(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the total todo-item count for the user with the specified ID from the backend API.
|
||||
* Creates an Endpoint which retrieves the total todo-item count for the user with the specified ID from the backend API.
|
||||
*
|
||||
* @param {string} jwt - The JWT appended as a bearer token to authorize the request.
|
||||
* @throws{error} - If the request fails or is not permitted.
|
||||
* @param {number} userId - The ID of the user whose todo-items to retrieve.
|
||||
* @throws {error} If the user is not currently logged in.
|
||||
*/
|
||||
export async function readTodoCount(
|
||||
userId: number,
|
||||
jwt: string = getTokenOrError()
|
||||
): Promise<number> {
|
||||
const endpoint = `/api/todos/user/${userId}/total/`;
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await getResponseBodyOrError(response);
|
||||
const itemCount = responseJson as ItemCount;
|
||||
return itemCount.total;
|
||||
createReadTodoCountEndpoint(userId: number): Endpoint<ItemCount> {
|
||||
return new Endpoint<ItemCount>(
|
||||
`/api/todos/user/${userId}/total/`,
|
||||
'GET',
|
||||
this._getDefaultHeadersWithAuth(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the todo-item with the specified ID from the backend API.
|
||||
* Creates an Endpoint which retrieves the todo-item with the specified ID from the backend API.
|
||||
*
|
||||
* @param {number} todoId - The ID of the todo-item to be retrieved.
|
||||
* @throws {error} If the user is not currently logged in.
|
||||
*/
|
||||
createReadTodoEndpoint(todoId: number): Endpoint<TodoItem> {
|
||||
return new Endpoint<TodoItem>(
|
||||
`/api/todos/${todoId}`,
|
||||
'GET',
|
||||
this._getDefaultHeadersWithAuth(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Endpoint which sends an API request containing the specified login credentials
|
||||
* to the backend, and returns the JWT retrieved from the response on sucess.
|
||||
*
|
||||
* @param {string} jwt - The JWT appended as a bearer token to authorize the request.
|
||||
* @throws {error} - If the request fails or is not permitted.
|
||||
*/
|
||||
export async function readTodo(
|
||||
todoId: number,
|
||||
jwt: string = getTokenOrError()
|
||||
): Promise<TodoItem> {
|
||||
const endpoint = `/api/todos/${todoId}`;
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
createLoginEndpoint(): Endpoint<Token> {
|
||||
return new Endpoint<Token>(
|
||||
`/api/auth/login/`,
|
||||
'POST',
|
||||
this._getDefaultHeaders(),
|
||||
this.fetchFunction
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await getResponseBodyOrError(response);
|
||||
return responseJson as TodoItem;
|
||||
}
|
||||
|
@ -1,7 +1,23 @@
|
||||
import type { StringMapping } from '$lib/utils/types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
/**
|
||||
* An API response indicating an item count.
|
||||
*/
|
||||
export type ItemCount = {
|
||||
total: number
|
||||
}
|
||||
|
||||
/**
|
||||
* An API response indicating a token.
|
||||
*/
|
||||
export type Token = {
|
||||
token: string
|
||||
}
|
||||
|
||||
/**
|
||||
* An API response representing a User object.
|
||||
*/
|
||||
export type User = {
|
||||
id: number,
|
||||
email: string,
|
||||
@ -12,6 +28,9 @@ export type User = {
|
||||
is_admin: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* An API response representing a TodoItem object.
|
||||
*/
|
||||
export type TodoItem = {
|
||||
id: number,
|
||||
title: string,
|
||||
@ -21,3 +40,89 @@ export type TodoItem = {
|
||||
updated: Date,
|
||||
finished: Date,
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed API-request request types.
|
||||
*/
|
||||
type RequestMethod = 'GET' | 'POST' | 'UPDATE' | 'DELETE';
|
||||
|
||||
/**
|
||||
* Allowed options to an Enpoint's `call()`-method
|
||||
*/
|
||||
type EndpointCallOptions = {
|
||||
body?: any,
|
||||
queryParameters?: StringMapping
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an external API endpoint.
|
||||
*/
|
||||
export class Endpoint<T> {
|
||||
/** The URL of the API endpoint. */
|
||||
readonly url: string;
|
||||
|
||||
/** The request method to be used for API calls. */
|
||||
readonly requestMethod: RequestMethod;
|
||||
|
||||
/** The headers to be included in API requests. */
|
||||
readonly requestHeaders: StringMapping;
|
||||
|
||||
/** The function to use for making API requests. */
|
||||
readonly fetchFunction: Function;
|
||||
|
||||
/**
|
||||
* Constructs a new `Endpoint` instance.
|
||||
* @param url - The URL of the API endpoint.
|
||||
* @param requestMethod - The request method to be used for API calls. (Default: 'GET')
|
||||
* @param requestHeaders - The headers to be included in API requests. (Default: {})
|
||||
* @param fetchFunction - The function to use for making API requests. (Default: fetch)
|
||||
*/
|
||||
constructor(
|
||||
url: string,
|
||||
requestMethod: RequestMethod = 'GET',
|
||||
requestHeaders: StringMapping = {},
|
||||
fetchFunction: Function = fetch
|
||||
) {
|
||||
this.url = url;
|
||||
this.requestMethod = requestMethod;
|
||||
this.requestHeaders = requestHeaders;
|
||||
this.fetchFunction = fetchFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the API endpoint with optional query parameters.
|
||||
*
|
||||
* @param queryParameters - The query parameters to be included in the API call. (Default: new URLSearchParams({}))
|
||||
* @returns A Promise that resolves to the JSON response from the API.
|
||||
* @throws {error} If the API request fails or returns an error response.
|
||||
*/
|
||||
async call(options: EndpointCallOptions = {}): Promise<T> {
|
||||
let endpointUrl = this.url;
|
||||
if ('queryParameters' in options) {
|
||||
endpointUrl += `?${new URLSearchParams(options.queryParameters)}`;
|
||||
}
|
||||
console.log(endpointUrl)
|
||||
|
||||
const response = await this.fetchFunction(endpointUrl, {
|
||||
method: this.requestMethod,
|
||||
headers: this.requestHeaders,
|
||||
body: 'body' in options ? JSON.stringify(options.body) : null
|
||||
});
|
||||
const responseJson = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
if ('detail' in responseJson) {
|
||||
if (typeof responseJson.detail === 'string') {
|
||||
throw error(response.status, responseJson.detail);
|
||||
} else if (Array.isArray(responseJson.detail)) {
|
||||
if ('msg' in responseJson.detail[0] && typeof responseJson.detail[0].msg === 'string') {
|
||||
throw error(response.status, responseJson.detail[0].msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error(response.status, `API request failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return responseJson as T;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getResponseBodyOrError } from '$lib/api/utils';
|
||||
import { readUser } from '$lib/api/endpoints';
|
||||
import { EndpointFactory } from '$lib/api/endpoints';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
@ -70,35 +70,6 @@ function clearUserInLocalstorage(): void {
|
||||
localStorage.removeItem(userKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an API request containing the specified login credentials to the backend,
|
||||
* and returns the JWT retrieved from the response on sucess.
|
||||
*
|
||||
* @param {string} email - The email address of the user whose JWT to retrieve.
|
||||
* @param {string} password - The password used to authenticate the user whose JWT is being retrieved.
|
||||
* @throws {Error} - If the API request failed or the supplied credentials were invalid.
|
||||
*/
|
||||
async function requestJwt(email: string, password: string): Promise<string> {
|
||||
type Token = {
|
||||
token: string
|
||||
}
|
||||
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
});
|
||||
|
||||
const responseBody = await getResponseBodyOrError(response) as Token;
|
||||
return responseBody.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the user currently saved in localstorage into the storedUser
|
||||
* svelte store.
|
||||
@ -125,18 +96,30 @@ function clearUserFromStore(): void {
|
||||
* @throws {Error} - If any API request fails or the supplied credentials were invalid.
|
||||
*/
|
||||
export async function login(email: string, password: string): Promise<void> {
|
||||
const token = await requestJwt(email, password);
|
||||
let endpointFactory = new EndpointFactory();
|
||||
const loginEndpoint = endpointFactory.createLoginEndpoint();
|
||||
const token = await loginEndpoint.call({
|
||||
body: {
|
||||
email: email,
|
||||
password: password,
|
||||
}
|
||||
});
|
||||
|
||||
interface Token {
|
||||
sub: number,
|
||||
exp: number
|
||||
}
|
||||
const parsedToken = jwt_decode(token) as Token;
|
||||
const parsedToken = jwt_decode(token.token) as Token;
|
||||
const userId = parsedToken.sub;
|
||||
|
||||
const user = await readUser(userId, token);
|
||||
saveTokenToLocalstorage(token.token);
|
||||
// recreate the factory with the jwt now in localstorage
|
||||
endpointFactory = new EndpointFactory();
|
||||
|
||||
const readUserEndpoint = endpointFactory.createReadUserEndpoint(userId);
|
||||
console.log(readUserEndpoint.call())
|
||||
const user = await readUserEndpoint.call();
|
||||
|
||||
saveTokenToLocalstorage(token);
|
||||
saveUserToLocalstorage({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
|
@ -3,8 +3,9 @@
|
||||
import { Icon, ChevronRight, ChevronLeft, ChevronDoubleRight, ChevronDoubleLeft } from 'svelte-hero-icons';
|
||||
import Th from '$lib/components/data-table/Th.svelte';
|
||||
import Td from '$lib/components/data-table/Td.svelte';
|
||||
import type { Column, Endpoint } from '$lib/components/data-table/types';
|
||||
import type { Column } from '$lib/components/data-table/types';
|
||||
import { interpolateString as i } from '$lib/components/data-table/utils';
|
||||
import type { Endpoint, ItemCount } from '$lib/api/types';
|
||||
|
||||
/**
|
||||
* The caption for the data table.
|
||||
@ -19,19 +20,21 @@
|
||||
* The function which fetches rows for the data table.
|
||||
* Must support pagination and sorting.
|
||||
*/
|
||||
export let getItemsEndpoint: Endpoint = {
|
||||
callable: () => [],
|
||||
args: [],
|
||||
}
|
||||
export let getItemsEndpoint: Endpoint<any>;
|
||||
|
||||
/**
|
||||
* The function which fetches the total number of items. Used for pagination.
|
||||
*/
|
||||
export let getItemCountEndpoint: Endpoint = {
|
||||
callable: () => 0,
|
||||
args: [],
|
||||
}
|
||||
export let getItemCountEndpoint: Endpoint<ItemCount>;
|
||||
|
||||
/**
|
||||
* The name of the field by which table entries are sorted by default.
|
||||
*/
|
||||
export let defaultSortField: string = columns[0].field;
|
||||
|
||||
/**
|
||||
* The sort order by which table entries are sorted by default.
|
||||
*/
|
||||
export let defaultSortOrder: 'asc' | 'desc' = 'asc';
|
||||
|
||||
let viewportWidth: number = 0;
|
||||
@ -139,16 +142,20 @@
|
||||
async function updateTable() {
|
||||
try {
|
||||
currentState = 'loading';
|
||||
totalItemCount = await getItemCountEndpoint.callable(
|
||||
...getItemCountEndpoint.args,
|
||||
);
|
||||
currentItems = await getItemsEndpoint.callable(
|
||||
...getItemsEndpoint.args,
|
||||
currentItemsOffset,
|
||||
currentItemsPerPage,
|
||||
currentSortField,
|
||||
currentSortOrder
|
||||
);
|
||||
|
||||
let itemCountResponse = await getItemCountEndpoint.call();
|
||||
totalItemCount = itemCountResponse.total;
|
||||
|
||||
let itemsResponse = await getItemsEndpoint.call({
|
||||
queryParameters: {
|
||||
skip: `${currentItemsOffset}`,
|
||||
limit: `${currentItemsPerPage}`,
|
||||
sortby: `${currentSortField}`,
|
||||
sortorder: `${currentSortOrder}`,
|
||||
}
|
||||
});
|
||||
currentItems = itemsResponse;
|
||||
|
||||
currentState = 'finished';
|
||||
} catch (error: any) {
|
||||
if (typeof error.body.message === 'string') {
|
||||
|
@ -6,8 +6,3 @@ export type Column = {
|
||||
isLink?: boolean,
|
||||
linkTarget?: string,
|
||||
}
|
||||
|
||||
export type Endpoint = {
|
||||
callable: Function,
|
||||
args: any[],
|
||||
}
|
||||
|
6
frontend/src/lib/utils/types.ts
Normal file
6
frontend/src/lib/utils/types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* A generic string-to-string mapping.
|
||||
*/
|
||||
export type StringMapping = {
|
||||
[key: string]: string;
|
||||
}
|
@ -1,18 +1,19 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { EndpointFactory } from '$lib/api/endpoints';
|
||||
import type { TodoItem } from '$lib/api/types';
|
||||
import { readTodo } from '$lib/api/endpoints';
|
||||
|
||||
export const ssr = false;
|
||||
export const load = (async ({ params }) => {
|
||||
// check if user exists
|
||||
const todoId = params.todo;
|
||||
const todo = await readTodo(todoId);
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ fetch, params }) {
|
||||
|
||||
let endpointFactory = new EndpointFactory(fetch);
|
||||
let readTodoEndpoint = endpointFactory.createReadTodoEndpoint(params.todo);
|
||||
|
||||
return {
|
||||
todo: todo
|
||||
endpointFactory: endpointFactory,
|
||||
todo: readTodoEndpoint.call(),
|
||||
};
|
||||
|
||||
}) satisfies PageLoad;
|
||||
}
|
||||
|
||||
export interface TodoDetailPage {
|
||||
endpointFactory: EndpointFactory,
|
||||
todo: TodoItem
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { UserProfilePage } from "./+page";
|
||||
import { getUserFromLocalstorage as getUser } from '$lib/auth/session';
|
||||
import { formatTime } from '$lib/utils/utils';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let data: UserProfilePage;
|
||||
const user = getUser();
|
||||
const loggedInUser = getUser();
|
||||
if (loggedInUser === null) {
|
||||
throw error(401, 'You must be logged in to view this page.');
|
||||
}
|
||||
|
||||
const userCreatedDate = Date.parse(data.user.created);
|
||||
let dateNow: number;
|
||||
@ -23,7 +27,7 @@
|
||||
</script>
|
||||
|
||||
<div class="grow flex flex-col items-center justify-center gap-y-4 px-4">
|
||||
{#if user.id === data.user.id}
|
||||
{#if loggedInUser.id === data.user.id}
|
||||
<h1>My Profile</h1>
|
||||
{:else}
|
||||
<h1>{`${data.user.first_name} ${data.user.last_name}'s Profile`}</h1>
|
||||
|
@ -1,18 +1,19 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { EndpointFactory } from '$lib/api/endpoints';
|
||||
import type { User } from '$lib/api/types';
|
||||
import { readUser } from '$lib/api/endpoints';
|
||||
|
||||
export const ssr = false;
|
||||
export const load = (async ({ params }) => {
|
||||
// check if user exists
|
||||
const userId = params.user;
|
||||
const user = await readUser(userId);
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ fetch, params }) {
|
||||
|
||||
let endpointFactory = new EndpointFactory(fetch);
|
||||
let readUserEndpoint = endpointFactory.createReadUserEndpoint(params.user);
|
||||
|
||||
return {
|
||||
user: user
|
||||
endpointFactory: endpointFactory,
|
||||
user: readUserEndpoint.call()
|
||||
};
|
||||
|
||||
}) satisfies PageLoad;
|
||||
}
|
||||
|
||||
export interface UserProfilePage {
|
||||
endpointFactory: EndpointFactory,
|
||||
user: User
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { error } from '@sveltejs/kit';
|
||||
import Table from '$lib/components/data-table/Table.svelte'
|
||||
import type { Column } from '$lib/components/data-table/types';
|
||||
import type { UserTodosPage } from "./+page";
|
||||
import { readTodos, readTodoCount } from '$lib/api/endpoints';
|
||||
import type { UserProfilePage } from "../+page";
|
||||
import { getUserFromLocalstorage as getUser } from '$lib/auth/session';
|
||||
|
||||
export let data: UserTodosPage;
|
||||
const user = getUser();
|
||||
export let data: UserProfilePage;
|
||||
const loggedInUser = getUser();
|
||||
if (loggedInUser === null) {
|
||||
throw error(401, 'You must be logged in to view this page.');
|
||||
}
|
||||
|
||||
const columns: Column[] = [
|
||||
{
|
||||
@ -43,20 +46,10 @@
|
||||
];
|
||||
|
||||
const table = {
|
||||
caption: user.id === data.user.id ? 'My TODOs' : `${data.user.first_name} ${data.user.last_name}'s TODOs`,
|
||||
columns: user.isAdmin ? columns : columns.slice(1, -1),
|
||||
getItemsEndpoint: {
|
||||
callable: readTodos,
|
||||
args: [
|
||||
data.user.id
|
||||
]
|
||||
},
|
||||
getItemCountEndpoint: {
|
||||
callable: readTodoCount,
|
||||
args: [
|
||||
data.user.id
|
||||
]
|
||||
},
|
||||
caption: loggedInUser.id === data.user.id ? 'My TODOs' : `${data.user.first_name} ${data.user.last_name}'s TODOs`,
|
||||
columns: loggedInUser.isAdmin ? columns : columns.slice(1, -1),
|
||||
getItemsEndpoint: data.endpointFactory.createReadTodosEndpoint(data.user.id),
|
||||
getItemCountEndpoint: data.endpointFactory.createReadTodoCountEndpoint(data.user.id),
|
||||
defaultSortField: 'updated',
|
||||
defaultSortOrder: 'desc',
|
||||
};
|
||||
|
@ -1,9 +0,0 @@
|
||||
import type { User } from '$lib/api/types';
|
||||
import { load as defaultLoad } from '../+page';
|
||||
|
||||
export const ssr = false;
|
||||
export const load = defaultLoad;
|
||||
|
||||
export interface UserTodosPage {
|
||||
user: User
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import Table from '$lib/components/data-table/Table.svelte';
|
||||
import type { Column } from '$lib/components/data-table/types';
|
||||
import { readUsers, readUserCount } from '$lib/api/endpoints';
|
||||
import { onMount } from 'svelte';
|
||||
import type { UserListPage } from "./+page";
|
||||
|
||||
export let data: UserListPage;
|
||||
|
||||
const columns: Column[] = [
|
||||
{
|
||||
@ -44,14 +45,8 @@
|
||||
const table = {
|
||||
caption: "List of users",
|
||||
columns: columns,
|
||||
getItemsEndpoint: {
|
||||
callable: readUsers,
|
||||
args: []
|
||||
},
|
||||
getItemCountEndpoint: {
|
||||
callable: readUserCount,
|
||||
args: []
|
||||
},
|
||||
getItemsEndpoint: data.endpointFactory.createReadUsersEndpoint(),
|
||||
getItemCountEndpoint: data.endpointFactory.createReadUserCountEndpoint(),
|
||||
defaultSortField: 'id',
|
||||
defaultSortOrder: 'asc',
|
||||
};
|
||||
|
19
frontend/src/routes/users/all/+page.ts
Normal file
19
frontend/src/routes/users/all/+page.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { EndpointFactory } from '$lib/api/endpoints';
|
||||
import type { User } from '$lib/api/types';
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ fetch }) {
|
||||
|
||||
let endpointFactory = new EndpointFactory(fetch);
|
||||
let readUsersEndpoint = endpointFactory.createReadUsersEndpoint();
|
||||
|
||||
return {
|
||||
endpointFactory: endpointFactory,
|
||||
users: readUsersEndpoint.call()
|
||||
};
|
||||
}
|
||||
|
||||
export interface UserListPage {
|
||||
endpointFactory: EndpointFactory,
|
||||
users: User[]
|
||||
}
|
Loading…
Reference in New Issue
Block a user