feat: implement get label by ID functionality in label management
All checks were successful
Frontend CI / build (push) Successful in 1m38s
All checks were successful
Frontend CI / build (push) Successful in 1m38s
This commit is contained in:
parent
d20a210857
commit
4f23151439
@ -31,6 +31,8 @@ import { CreatePostUseCase } from '$lib/post/application/useCase/createPostUseCa
|
|||||||
import { GetAllPostsUseCase } from '$lib/post/application/useCase/getAllPostsUseCase';
|
import { GetAllPostsUseCase } from '$lib/post/application/useCase/getAllPostsUseCase';
|
||||||
import { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase';
|
import { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase';
|
||||||
import { PostApiServiceImpl } from '$lib/post/framework/api/postApiServiceImpl';
|
import { PostApiServiceImpl } from '$lib/post/framework/api/postApiServiceImpl';
|
||||||
|
import { GetLabelUseCase } from '$lib/label/application/useCase/getLabelUseCase';
|
||||||
|
import { LabelLoadedStore } from '$lib/label/adapter/presenter/labelLoadedStore';
|
||||||
|
|
||||||
export class Container {
|
export class Container {
|
||||||
private useCases: UseCases;
|
private useCases: UseCases;
|
||||||
@ -65,6 +67,10 @@ export class Container {
|
|||||||
return new LabelsListedStore(this.useCases.getAllLabelsUseCase, initialData);
|
return new LabelsListedStore(this.useCases.getAllLabelsUseCase, initialData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createLabelLoadedStore(initialData?: LabelViewModel): LabelLoadedStore {
|
||||||
|
return new LabelLoadedStore(this.useCases.getLabelUseCase, initialData);
|
||||||
|
}
|
||||||
|
|
||||||
createLabelCreatedStore(): LabelCreatedStore {
|
createLabelCreatedStore(): LabelCreatedStore {
|
||||||
return new LabelCreatedStore(this.useCases.createLabelUseCase);
|
return new LabelCreatedStore(this.useCases.createLabelUseCase);
|
||||||
}
|
}
|
||||||
@ -145,6 +151,7 @@ class UseCases {
|
|||||||
private _getPostUseCase?: GetPostUseCase;
|
private _getPostUseCase?: GetPostUseCase;
|
||||||
private _createPostUseCase?: CreatePostUseCase;
|
private _createPostUseCase?: CreatePostUseCase;
|
||||||
private _getAllLabelsUseCase?: GetAllLabelsUseCase;
|
private _getAllLabelsUseCase?: GetAllLabelsUseCase;
|
||||||
|
private _getLabelUseCase?: GetLabelUseCase;
|
||||||
private _createLabelUseCase?: CreateLabelUseCase;
|
private _createLabelUseCase?: CreateLabelUseCase;
|
||||||
|
|
||||||
constructor(repositories: Repositories) {
|
constructor(repositories: Repositories) {
|
||||||
@ -181,6 +188,11 @@ class UseCases {
|
|||||||
return this._getAllLabelsUseCase;
|
return this._getAllLabelsUseCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get getLabelUseCase(): GetLabelUseCase {
|
||||||
|
this._getLabelUseCase ??= new GetLabelUseCase(this.repositories.labelRepository);
|
||||||
|
return this._getLabelUseCase;
|
||||||
|
}
|
||||||
|
|
||||||
get createLabelUseCase(): CreateLabelUseCase {
|
get createLabelUseCase(): CreateLabelUseCase {
|
||||||
this._createLabelUseCase ??= new CreateLabelUseCase(this.repositories.labelRepository);
|
this._createLabelUseCase ??= new CreateLabelUseCase(this.repositories.labelRepository);
|
||||||
return this._createLabelUseCase;
|
return this._createLabelUseCase;
|
||||||
|
@ -3,5 +3,6 @@ import type { LabelResponseDto } from '$lib/label/adapter/gateway/labelResponseD
|
|||||||
|
|
||||||
export interface LabelApiService {
|
export interface LabelApiService {
|
||||||
getAllLabels(): Promise<LabelResponseDto[]>;
|
getAllLabels(): Promise<LabelResponseDto[]>;
|
||||||
|
getLabelById(id: number): Promise<LabelResponseDto | null>;
|
||||||
createLabel(payload: CreateLabelRequestDto): Promise<LabelResponseDto>;
|
createLabel(payload: CreateLabelRequestDto): Promise<LabelResponseDto>;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,11 @@ export class LabelRepositoryImpl implements LabelRepository {
|
|||||||
return dtos.map((dto) => dto.toEntity());
|
return dtos.map((dto) => dto.toEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLabelById(id: number): Promise<Label | null> {
|
||||||
|
const dto = await this.labelApiService.getLabelById(id);
|
||||||
|
return dto?.toEntity() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
async createLabel(params: CreateLabelParams): Promise<Label> {
|
async createLabel(params: CreateLabelParams): Promise<Label> {
|
||||||
const requestDto = CreateLabelRequestDto.fromParams(params);
|
const requestDto = CreateLabelRequestDto.fromParams(params);
|
||||||
const responseDto = await this.labelApiService.createLabel(requestDto);
|
const responseDto = await this.labelApiService.createLabel(requestDto);
|
||||||
|
51
frontend/src/lib/label/adapter/presenter/labelLoadedStore.ts
Normal file
51
frontend/src/lib/label/adapter/presenter/labelLoadedStore.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { AsyncState } from '$lib/common/adapter/presenter/asyncState';
|
||||||
|
import type { BaseStore } from '$lib/common/adapter/presenter/baseStore';
|
||||||
|
import { LabelViewModel } from '$lib/label/adapter/presenter/labelViewModel';
|
||||||
|
import type { GetLabelUseCase } from '$lib/label/application/useCase/getLabelUseCase';
|
||||||
|
import { captureException } from '@sentry/sveltekit';
|
||||||
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
|
type LabelState = AsyncState<LabelViewModel>;
|
||||||
|
|
||||||
|
export class LabelLoadedStore implements BaseStore<LabelState, number> {
|
||||||
|
private state = writable<LabelState>(AsyncState.idle<LabelViewModel>(null));
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly getLabelUseCase: GetLabelUseCase,
|
||||||
|
initialData?: LabelViewModel
|
||||||
|
) {
|
||||||
|
if (initialData) {
|
||||||
|
this.state.set(AsyncState.idle(initialData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get subscribe() {
|
||||||
|
return this.state.subscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
get trigger() {
|
||||||
|
return (id: number) => this.loadLabel(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadLabel(id: number): Promise<LabelState> {
|
||||||
|
this.state.set(AsyncState.loading<LabelViewModel>(get(this.state).data));
|
||||||
|
|
||||||
|
let result: LabelState;
|
||||||
|
try {
|
||||||
|
const label = await this.getLabelUseCase.execute(id);
|
||||||
|
if (!label) {
|
||||||
|
result = AsyncState.error(new Error('Label not found'), get(this.state).data);
|
||||||
|
this.state.set(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const labelViewModel = LabelViewModel.fromEntity(label);
|
||||||
|
result = AsyncState.success(labelViewModel);
|
||||||
|
} catch (e) {
|
||||||
|
result = AsyncState.error(e, get(this.state).data);
|
||||||
|
captureException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.set(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import type { Label } from '$lib/label/domain/entity/label';
|
|||||||
|
|
||||||
export interface LabelRepository {
|
export interface LabelRepository {
|
||||||
getAllLabels(): Promise<Label[]>;
|
getAllLabels(): Promise<Label[]>;
|
||||||
|
getLabelById(id: number): Promise<Label | null>;
|
||||||
createLabel(params: CreateLabelParams): Promise<Label>;
|
createLabel(params: CreateLabelParams): Promise<Label>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import type { Label } from '$lib/label/domain/entity/label';
|
|||||||
export class GetAllLabelsUseCase {
|
export class GetAllLabelsUseCase {
|
||||||
constructor(private readonly labelRepository: LabelRepository) {}
|
constructor(private readonly labelRepository: LabelRepository) {}
|
||||||
|
|
||||||
execute(): Promise<Label[]> {
|
async execute(): Promise<Label[]> {
|
||||||
return this.labelRepository.getAllLabels();
|
return this.labelRepository.getAllLabels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
import type { LabelRepository } from '$lib/label/application/gateway/labelRepository';
|
||||||
|
import type { Label } from '$lib/label/domain/entity/label';
|
||||||
|
|
||||||
|
export class GetLabelUseCase {
|
||||||
|
constructor(private readonly labelRepository: LabelRepository) {}
|
||||||
|
|
||||||
|
async execute(id: number): Promise<Label | null> {
|
||||||
|
return this.labelRepository.getLabelById(id);
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,22 @@ export class LabelApiServiceImpl implements LabelApiService {
|
|||||||
return data.map(LabelResponseDto.fromJson);
|
return data.map(LabelResponseDto.fromJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLabelById(id: number): Promise<LabelResponseDto | null> {
|
||||||
|
const url = new URL(`label/${id}`, Environment.API_BASE_URL);
|
||||||
|
|
||||||
|
const response = await this.fetchFn(url);
|
||||||
|
|
||||||
|
if (response.status === 404) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new HttpError(response.status, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return LabelResponseDto.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
async createLabel(payload: CreateLabelRequestDto): Promise<LabelResponseDto> {
|
async createLabel(payload: CreateLabelRequestDto): Promise<LabelResponseDto> {
|
||||||
const url = new URL('label', Environment.API_BASE_URL);
|
const url = new URL('label', Environment.API_BASE_URL);
|
||||||
|
|
||||||
|
23
frontend/src/routes/dashboard/label/[id]/+page.server.ts
Normal file
23
frontend/src/routes/dashboard/label/[id]/+page.server.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ locals, params }) => {
|
||||||
|
const { container } = locals;
|
||||||
|
const store = container.createLabelLoadedStore();
|
||||||
|
|
||||||
|
const { trigger: loadLabel } = store;
|
||||||
|
|
||||||
|
const numericId = Number(params.id);
|
||||||
|
if (isNaN(numericId)) {
|
||||||
|
throw error(400, { message: 'Invalid label ID' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = await loadLabel(numericId);
|
||||||
|
if (!state.data) {
|
||||||
|
error(404, { message: 'Post not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dehydratedData: state.data.dehydrate(),
|
||||||
|
};
|
||||||
|
};
|
17
frontend/src/routes/dashboard/label/[id]/+page.svelte
Normal file
17
frontend/src/routes/dashboard/label/[id]/+page.svelte
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getContext, setContext } from 'svelte';
|
||||||
|
import type { PageProps } from './$types';
|
||||||
|
import { Container } from '$lib/container';
|
||||||
|
import { LabelViewModel } from '$lib/label/adapter/presenter/labelViewModel';
|
||||||
|
import { LabelLoadedStore } from '$lib/label/adapter/presenter/labelLoadedStore';
|
||||||
|
|
||||||
|
const { data, params }: PageProps = $props();
|
||||||
|
const { id } = params;
|
||||||
|
const container = getContext<Container>(Container.name);
|
||||||
|
|
||||||
|
const initialData = LabelViewModel.rehydrate(data.dehydratedData!);
|
||||||
|
const store = container.createLabelLoadedStore(initialData);
|
||||||
|
setContext(LabelLoadedStore.name, store);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>{id}</div>
|
Loading…
x
Reference in New Issue
Block a user