BLOG-126 Post management (list and create) #139
@ -17,6 +17,7 @@ import type { PostInfoViewModel } from '$lib/post/adapter/presenter/postInfoView
|
|||||||
import { PostListBloc } from '$lib/post/adapter/presenter/postListBloc';
|
import { PostListBloc } from '$lib/post/adapter/presenter/postListBloc';
|
||||||
import type { PostViewModel } from '$lib/post/adapter/presenter/postViewModel';
|
import type { PostViewModel } from '$lib/post/adapter/presenter/postViewModel';
|
||||||
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
|
import { CreatePostUseCase } from '$lib/post/application/useCase/createPostUseCase';
|
||||||
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';
|
||||||
@ -43,7 +44,7 @@ export class Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createPostBloc(initialData?: PostViewModel): PostBloc {
|
createPostBloc(initialData?: PostViewModel): PostBloc {
|
||||||
return new PostBloc(this.useCases.getPostUseCase, initialData);
|
return new PostBloc(this.useCases.getPostUseCase, this.useCases.createPostUseCase, initialData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ class UseCases {
|
|||||||
private _uploadImageUseCase?: UploadImageUseCase;
|
private _uploadImageUseCase?: UploadImageUseCase;
|
||||||
private _getAllPostsUseCase?: GetAllPostsUseCase;
|
private _getAllPostsUseCase?: GetAllPostsUseCase;
|
||||||
private _getPostUseCase?: GetPostUseCase;
|
private _getPostUseCase?: GetPostUseCase;
|
||||||
|
private _createPostUseCase?: CreatePostUseCase;
|
||||||
|
|
||||||
constructor(repositories: Repositories) {
|
constructor(repositories: Repositories) {
|
||||||
this.repositories = repositories;
|
this.repositories = repositories;
|
||||||
@ -127,8 +129,14 @@ class UseCases {
|
|||||||
this._getAllPostsUseCase ??= new GetAllPostsUseCase(this.repositories.postRepository);
|
this._getAllPostsUseCase ??= new GetAllPostsUseCase(this.repositories.postRepository);
|
||||||
return this._getAllPostsUseCase;
|
return this._getAllPostsUseCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
get getPostUseCase(): GetPostUseCase {
|
get getPostUseCase(): GetPostUseCase {
|
||||||
this._getPostUseCase ??= new GetPostUseCase(this.repositories.postRepository);
|
this._getPostUseCase ??= new GetPostUseCase(this.repositories.postRepository);
|
||||||
return this._getPostUseCase;
|
return this._getPostUseCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get createPostUseCase(): CreatePostUseCase {
|
||||||
|
this._createPostUseCase ??= new CreatePostUseCase(this.repositories.postRepository);
|
||||||
|
return this._createPostUseCase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,13 +40,9 @@
|
|||||||
files = undefined;
|
files = undefined;
|
||||||
fileInputErrorMessage = null;
|
fileInputErrorMessage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
|
||||||
open = false;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog {open} onOpenChange={(val) => (open = val)}>
|
<Dialog bind:open>
|
||||||
<DialogTrigger class={buttonVariants({ variant: 'default' })}>Upload</DialogTrigger>
|
<DialogTrigger class={buttonVariants({ variant: 'default' })}>Upload</DialogTrigger>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
showCloseButton={false}
|
showCloseButton={false}
|
||||||
@ -76,7 +72,7 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<DialogFooter class="mt-6">
|
<DialogFooter class="mt-6">
|
||||||
<Button variant="outline" onclick={close} {disabled}>Cancel</Button>
|
<Button variant="outline" onclick={() => (open = false)} {disabled}>Cancel</Button>
|
||||||
<Button type="submit" form="upload-image-form" {disabled}>Submit</Button>
|
<Button type="submit" form="upload-image-form" {disabled}>Submit</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
52
frontend/src/lib/post/adapter/gateway/creatPostRequestDto.ts
Normal file
52
frontend/src/lib/post/adapter/gateway/creatPostRequestDto.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import type { CreatePostParams } from '$lib/post/application/gateway/postRepository';
|
||||||
|
|
||||||
|
export class CreatePostRequestDto {
|
||||||
|
readonly semanticId: string;
|
||||||
|
readonly title: string;
|
||||||
|
readonly description: string;
|
||||||
|
readonly content: string;
|
||||||
|
readonly labelIds: number[];
|
||||||
|
readonly previewImageUrl: URL;
|
||||||
|
readonly publishedTime: Date | null;
|
||||||
|
|
||||||
|
private constructor(props: {
|
||||||
|
semanticId: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
content: string;
|
||||||
|
labelIds: number[];
|
||||||
|
previewImageUrl: URL;
|
||||||
|
publishedTime: Date | null;
|
||||||
|
}) {
|
||||||
|
this.semanticId = props.semanticId;
|
||||||
|
this.title = props.title;
|
||||||
|
this.description = props.description;
|
||||||
|
this.content = props.content;
|
||||||
|
this.labelIds = props.labelIds;
|
||||||
|
this.previewImageUrl = props.previewImageUrl;
|
||||||
|
this.publishedTime = props.publishedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromParams(params: CreatePostParams): CreatePostRequestDto {
|
||||||
|
return new CreatePostRequestDto({
|
||||||
|
...params,
|
||||||
|
description: '',
|
||||||
|
content: '',
|
||||||
|
labelIds: [],
|
||||||
|
previewImageUrl: new URL('https://example.com'),
|
||||||
|
publishedTime: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
semantic_id: this.semanticId,
|
||||||
|
title: this.title,
|
||||||
|
description: this.description,
|
||||||
|
content: this.content,
|
||||||
|
label_ids: this.labelIds,
|
||||||
|
preview_image_url: this.previewImageUrl,
|
||||||
|
published_time: this.publishedTime,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
|
import type { CreatePostRequestDto } from '$lib/post/adapter/gateway/creatPostRequestDto';
|
||||||
import type { PostInfoResponseDto } from '$lib/post/adapter/gateway/postInfoResponseDto';
|
import type { PostInfoResponseDto } from '$lib/post/adapter/gateway/postInfoResponseDto';
|
||||||
import type { PostResponseDto } from '$lib/post/adapter/gateway/postResponseDto';
|
import type { PostResponseDto } from '$lib/post/adapter/gateway/postResponseDto';
|
||||||
|
|
||||||
export interface PostApiService {
|
export interface PostApiService {
|
||||||
getAllPosts(): Promise<PostInfoResponseDto[]>;
|
getAllPosts(): Promise<PostInfoResponseDto[]>;
|
||||||
getPost(id: string): Promise<PostResponseDto | null>;
|
getPost(id: string): Promise<PostResponseDto | null>;
|
||||||
|
createPost(payload: CreatePostRequestDto): Promise<PostResponseDto>;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { CreatePostRequestDto } from '$lib/post/adapter/gateway/creatPostRequestDto';
|
||||||
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
||||||
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
import type { CreatePostParams, PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
import type { Post } from '$lib/post/domain/entity/post';
|
import type { Post } from '$lib/post/domain/entity/post';
|
||||||
import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
||||||
|
|
||||||
@ -15,4 +16,10 @@ export class PostRepositoryImpl implements PostRepository {
|
|||||||
const dto = await this.postApiService.getPost(id);
|
const dto = await this.postApiService.getPost(id);
|
||||||
return dto?.toEntity() ?? null;
|
return dto?.toEntity() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createPost(params: CreatePostParams): Promise<Post> {
|
||||||
|
const requestDto = CreatePostRequestDto.fromParams(params);
|
||||||
|
const responseDto = await this.postApiService.createPost(requestDto);
|
||||||
|
return responseDto.toEntity();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,14 @@ import {
|
|||||||
type AsyncState,
|
type AsyncState,
|
||||||
} from '$lib/common/adapter/presenter/asyncState';
|
} from '$lib/common/adapter/presenter/asyncState';
|
||||||
import { PostViewModel } from '$lib/post/adapter/presenter/postViewModel';
|
import { PostViewModel } from '$lib/post/adapter/presenter/postViewModel';
|
||||||
|
import type { CreatePostParams } from '$lib/post/application/gateway/postRepository';
|
||||||
|
import type { CreatePostUseCase } from '$lib/post/application/useCase/createPostUseCase';
|
||||||
import type { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase';
|
import type { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase';
|
||||||
import { captureException } from '@sentry/sveltekit';
|
import { captureException } from '@sentry/sveltekit';
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
export type PostState = AsyncState<PostViewModel>;
|
export type PostState = AsyncState<PostViewModel>;
|
||||||
export type PostEvent = PostLoadedEvent;
|
export type PostEvent = PostLoadedEvent | PostCreatedEvent;
|
||||||
|
|
||||||
export class PostBloc {
|
export class PostBloc {
|
||||||
private readonly state = writable<PostState>({
|
private readonly state = writable<PostState>({
|
||||||
@ -18,6 +20,7 @@ export class PostBloc {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly getPostUseCase: GetPostUseCase,
|
private readonly getPostUseCase: GetPostUseCase,
|
||||||
|
private readonly createPostUseCase: CreatePostUseCase,
|
||||||
initialData?: PostViewModel
|
initialData?: PostViewModel
|
||||||
) {
|
) {
|
||||||
this.state.set({
|
this.state.set({
|
||||||
@ -34,6 +37,9 @@ export class PostBloc {
|
|||||||
switch (event.event) {
|
switch (event.event) {
|
||||||
case PostEventType.PostLoadedEvent:
|
case PostEventType.PostLoadedEvent:
|
||||||
return this.loadPost(event.id);
|
return this.loadPost(event.id);
|
||||||
|
case PostEventType.PostCreatedEvent:
|
||||||
|
const { semanticId, title } = event;
|
||||||
|
return this.createPost({ semanticId, title });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,13 +64,37 @@ export class PostBloc {
|
|||||||
this.state.set(result);
|
this.state.set(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createPost(params: CreatePostParams): Promise<PostState> {
|
||||||
|
this.state.set(StateFactory.loading(get(this.state).data));
|
||||||
|
|
||||||
|
let result: PostState;
|
||||||
|
try {
|
||||||
|
const post = await this.createPostUseCase.execute(params);
|
||||||
|
const postViewModel = PostViewModel.fromEntity(post);
|
||||||
|
result = StateFactory.success(postViewModel);
|
||||||
|
} catch (e) {
|
||||||
|
result = StateFactory.error(e);
|
||||||
|
captureException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.set(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PostEventType {
|
export enum PostEventType {
|
||||||
PostLoadedEvent,
|
PostLoadedEvent,
|
||||||
|
PostCreatedEvent,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PostLoadedEvent {
|
interface PostLoadedEvent {
|
||||||
event: PostEventType.PostLoadedEvent;
|
event: PostEventType.PostLoadedEvent;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PostCreatedEvent {
|
||||||
|
event: PostEventType.PostCreatedEvent;
|
||||||
|
semanticId: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
@ -4,4 +4,10 @@ import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
|||||||
export interface PostRepository {
|
export interface PostRepository {
|
||||||
getAllPosts(): Promise<PostInfo[]>;
|
getAllPosts(): Promise<PostInfo[]>;
|
||||||
getPost(id: string): Promise<Post | null>;
|
getPost(id: string): Promise<Post | null>;
|
||||||
|
createPost(params: CreatePostParams): Promise<Post>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePostParams {
|
||||||
|
semanticId: string;
|
||||||
|
title: string;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import type {
|
||||||
|
CreatePostParams,
|
||||||
|
PostRepository,
|
||||||
|
} from '$lib/post/application/gateway/postRepository';
|
||||||
|
import type { Post } from '$lib/post/domain/entity/post';
|
||||||
|
|
||||||
|
export class CreatePostUseCase {
|
||||||
|
constructor(private readonly postRepository: PostRepository) {}
|
||||||
|
|
||||||
|
async execute(params: CreatePostParams): Promise<Post> {
|
||||||
|
return this.postRepository.createPost(params);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { HttpError } from '$lib/common/framework/web/httpError';
|
import { HttpError } from '$lib/common/framework/web/httpError';
|
||||||
import { HttpStatusCode } from '$lib/common/framework/web/httpStatusCode';
|
import { HttpStatusCode } from '$lib/common/framework/web/httpStatusCode';
|
||||||
import { Environment } from '$lib/environment';
|
import { Environment } from '$lib/environment';
|
||||||
|
import type { CreatePostRequestDto } from '$lib/post/adapter/gateway/creatPostRequestDto';
|
||||||
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
||||||
import { PostInfoResponseDto } from '$lib/post/adapter/gateway/postInfoResponseDto';
|
import { PostInfoResponseDto } from '$lib/post/adapter/gateway/postInfoResponseDto';
|
||||||
import { PostResponseDto } from '$lib/post/adapter/gateway/postResponseDto';
|
import { PostResponseDto } from '$lib/post/adapter/gateway/postResponseDto';
|
||||||
@ -17,8 +18,8 @@ export class PostApiServiceImpl implements PostApiService {
|
|||||||
throw new HttpError(response.status, url);
|
throw new HttpError(response.status, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await response.json();
|
const data = await response.json();
|
||||||
return json.map(PostInfoResponseDto.fromJson);
|
return data.map(PostInfoResponseDto.fromJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPost(id: string): Promise<PostResponseDto | null> {
|
async getPost(id: string): Promise<PostResponseDto | null> {
|
||||||
@ -34,7 +35,24 @@ export class PostApiServiceImpl implements PostApiService {
|
|||||||
throw new HttpError(response.status, url);
|
throw new HttpError(response.status, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await response.json();
|
const data = await response.json();
|
||||||
return PostResponseDto.fromJson(json);
|
return PostResponseDto.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPost(payload: CreatePostRequestDto): Promise<PostResponseDto> {
|
||||||
|
const url = new URL('post', Environment.API_BASE_URL);
|
||||||
|
|
||||||
|
const response = await this.fetchFn(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload.toJson()),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new HttpError(response.status, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return PostResponseDto.fromJson(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
107
frontend/src/lib/post/framework/ui/CreatePostDialog.svelte
Normal file
107
frontend/src/lib/post/framework/ui/CreatePostDialog.svelte
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<script module lang="ts">
|
||||||
|
import z from 'zod';
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
semanticId: z
|
||||||
|
.string()
|
||||||
|
.max(100)
|
||||||
|
.regex(/\D/)
|
||||||
|
.regex(/^[a-zA-Z0-9_\-]+$/),
|
||||||
|
title: z.string().trim().nonempty().max(100),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormParams = z.infer<typeof formSchema>;
|
||||||
|
export type CreatePostDialogFormParams = FormParams;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Button, buttonVariants } from '$lib/common/framework/components/ui/button';
|
||||||
|
import { Dialog } from '$lib/common/framework/components/ui/dialog';
|
||||||
|
import DialogContent from '$lib/common/framework/components/ui/dialog/dialog-content.svelte';
|
||||||
|
import DialogFooter from '$lib/common/framework/components/ui/dialog/dialog-footer.svelte';
|
||||||
|
import DialogHeader from '$lib/common/framework/components/ui/dialog/dialog-header.svelte';
|
||||||
|
import DialogTitle from '$lib/common/framework/components/ui/dialog/dialog-title.svelte';
|
||||||
|
import DialogTrigger from '$lib/common/framework/components/ui/dialog/dialog-trigger.svelte';
|
||||||
|
import Input from '$lib/common/framework/components/ui/input/input.svelte';
|
||||||
|
import Label from '$lib/common/framework/components/ui/label/label.svelte';
|
||||||
|
|
||||||
|
const {
|
||||||
|
disabled,
|
||||||
|
onSubmit: createPost,
|
||||||
|
}: {
|
||||||
|
disabled: boolean;
|
||||||
|
onSubmit: (params: FormParams) => Promise<void>;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let open = $state(false);
|
||||||
|
|
||||||
|
let formData = $state<FormParams>({ semanticId: '', title: '' });
|
||||||
|
let formErrors = $state<Partial<Record<keyof FormParams, string>>>({});
|
||||||
|
|
||||||
|
async function onSubmit(event: SubmitEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
formErrors = {};
|
||||||
|
|
||||||
|
const parseResult = formSchema.safeParse(formData);
|
||||||
|
if (parseResult.error) {
|
||||||
|
const errorResult = z.treeifyError(parseResult.error).properties;
|
||||||
|
if (errorResult?.semanticId?.errors) {
|
||||||
|
formErrors.semanticId = errorResult.semanticId.errors[0];
|
||||||
|
}
|
||||||
|
if (errorResult?.title?.errors) {
|
||||||
|
formErrors.title = errorResult.title.errors[0];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await createPost(formData);
|
||||||
|
formData = { semanticId: '', title: '' };
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog bind:open>
|
||||||
|
<DialogTrigger class={buttonVariants({ variant: 'default' })}>Create</DialogTrigger>
|
||||||
|
<DialogContent
|
||||||
|
showCloseButton={false}
|
||||||
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
|
onEscapeKeydown={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<DialogHeader class="mb-4">
|
||||||
|
<DialogTitle>Create Post</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<form id="create-post-form" onsubmit={onSubmit} class="space-y-3">
|
||||||
|
<div>
|
||||||
|
<Label for="semantic-id-input" class="pb-2">Semantic ID</Label>
|
||||||
|
<Input
|
||||||
|
id="semantic-id-input"
|
||||||
|
type="text"
|
||||||
|
aria-invalid={formErrors.semanticId !== undefined}
|
||||||
|
bind:value={formData.semanticId}
|
||||||
|
/>
|
||||||
|
{#if formErrors.semanticId}
|
||||||
|
<p class="text-sm text-red-500">{formErrors.semanticId}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Label for="title-input" class="pb-2">Title</Label>
|
||||||
|
<Input
|
||||||
|
id="title-input"
|
||||||
|
type="text"
|
||||||
|
aria-invalid={formErrors.title !== undefined}
|
||||||
|
bind:value={formData.title}
|
||||||
|
/>
|
||||||
|
{#if formErrors.title}
|
||||||
|
<p class="text-sm text-red-500">{formErrors.title}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<DialogFooter class="mt-6">
|
||||||
|
<Button variant="outline" onclick={() => (open = false)} {disabled}>Cancel</Button>
|
||||||
|
<Button type="submit" form="create-post-form" {disabled}>Submit</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
35
frontend/src/lib/post/framework/ui/PostManagementPage.svelte
Normal file
35
frontend/src/lib/post/framework/ui/PostManagementPage.svelte
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { StatusType } from '$lib/common/adapter/presenter/asyncState';
|
||||||
|
import { PostBloc, PostEventType } from '$lib/post/adapter/presenter/postBloc';
|
||||||
|
import CreatePostDialog, {
|
||||||
|
type CreatePostDialogFormParams,
|
||||||
|
} from '$lib/post/framework/ui/CreatePostDialog.svelte';
|
||||||
|
import { getContext } from 'svelte';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
|
const postBloc = getContext<PostBloc>(PostBloc.name);
|
||||||
|
const state = $derived($postBloc);
|
||||||
|
|
||||||
|
const isLoading = $derived(state.status === StatusType.Loading);
|
||||||
|
|
||||||
|
async function onCreatePostDialogSubmit(params: CreatePostDialogFormParams) {
|
||||||
|
const state = await postBloc.dispatch({ event: PostEventType.PostCreatedEvent, ...params });
|
||||||
|
|
||||||
|
if (state.status === StatusType.Success) {
|
||||||
|
toast.success(`Post created successfully with ID: ${state.data.id}`);
|
||||||
|
} else if (state.status === StatusType.Error) {
|
||||||
|
toast.error('Failed to create post', {
|
||||||
|
description: state.error?.message ?? 'Unknown error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="dashboard-container mb-10">
|
||||||
|
<div class="flex flex-row items-center justify-between">
|
||||||
|
<h1 class="py-16 text-5xl font-bold text-gray-800">Post</h1>
|
||||||
|
<CreatePostDialog disabled={isLoading} onSubmit={onCreatePostDialogSubmit} />
|
||||||
|
</div>
|
||||||
|
<p></p>
|
||||||
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import ErrorPage from '$lib/common/framework/ui/ErrorPage.svelte';
|
import ErrorPage from '$lib/common/framework/ui/ErrorPage.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import HomePage from '$lib/home/framework/ui/HomePage.svelte';
|
import HomePage from '$lib/home/framework/ui/HomePage.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ImageApiService } from '$lib/image/adapter/gateway/imageApiService';
|
import { Container } from '$lib/container';
|
||||||
import { ImageRepositoryImpl } from '$lib/image/adapter/gateway/imageRepositoryImpl';
|
|
||||||
import { ImageBloc } from '$lib/image/adapter/presenter/imageBloc';
|
import { ImageBloc } from '$lib/image/adapter/presenter/imageBloc';
|
||||||
import type { ImageRepository } from '$lib/image/application/gateway/imageRepository';
|
|
||||||
import { UploadImageUseCase } from '$lib/image/application/useCase/uploadImageUseCase';
|
|
||||||
import { ImageApiServiceImpl } from '$lib/image/framework/api/imageApiServiceImpl';
|
|
||||||
import ImageManagementPage from '$lib/image/framework/ui/ImageManagementPage.svelte';
|
import ImageManagementPage from '$lib/image/framework/ui/ImageManagementPage.svelte';
|
||||||
import { setContext } from 'svelte';
|
import { getContext, setContext } from 'svelte';
|
||||||
|
|
||||||
const imageApiService: ImageApiService = new ImageApiServiceImpl(fetch);
|
|
||||||
const imageRepository: ImageRepository = new ImageRepositoryImpl(imageApiService);
|
|
||||||
const uploadImageUseCase = new UploadImageUseCase(imageRepository);
|
|
||||||
const imageBloc = new ImageBloc(uploadImageUseCase);
|
|
||||||
|
|
||||||
|
const container = getContext<Container>(Container.name);
|
||||||
|
const imageBloc = container.createImageBloc();
|
||||||
setContext(ImageBloc.name, imageBloc);
|
setContext(ImageBloc.name, imageBloc);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1 +1,12 @@
|
|||||||
<div>Post</div>
|
<script lang="ts">
|
||||||
|
import { Container } from '$lib/container';
|
||||||
|
import { PostBloc } from '$lib/post/adapter/presenter/postBloc';
|
||||||
|
import PostManagementPage from '$lib/post/framework/ui/PostManagementPage.svelte';
|
||||||
|
import { getContext, setContext } from 'svelte';
|
||||||
|
|
||||||
|
const container = getContext<Container>(Container.name);
|
||||||
|
const postBloc = container.createPostBloc();
|
||||||
|
setContext(PostBloc.name, postBloc);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<PostManagementPage />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user