diff --git a/apps/web/src/app/main/community/votesboard/components/VoteboardForm.tsx b/apps/web/src/app/main/community/votesboard/components/VoteboardForm.tsx index 22c5fbf..0c2075d 100644 --- a/apps/web/src/app/main/community/votesboard/components/VoteboardForm.tsx +++ b/apps/web/src/app/main/community/votesboard/components/VoteboardForm.tsx @@ -12,7 +12,7 @@ import { type VoteboardFormData, voteboardSchema, } from '../schema/voteboardSchema'; -import type { VotePostDetailResponse } from '@/generated/api/models'; +import type { VoteboardDetailResponse } from '@/generated/api/models'; import { Plus } from 'lucide-react'; import { VoteboardOptionField } from './VoteoptionField'; import { CATEGORIES, Category } from '../../constants/categories'; @@ -24,7 +24,7 @@ export interface VoteboardFormProps { /** 수정할 투표 게시글 ID (없으면 생성 모드) */ voteboardId?: number; /** 초기 폼 데이터 (수정 모드에서 사용) */ - initialData?: VotePostDetailResponse; + initialData?: VoteboardDetailResponse; /** 초기 선택된 카테고리 (생성 모드에서 사용) */ initialCategory?: Category; } diff --git a/apps/web/src/generated/api/endpoints/voteboard/voteboard.ts b/apps/web/src/generated/api/endpoints/voteboard/voteboard.ts index 82bb8ce..af143e3 100644 --- a/apps/web/src/generated/api/endpoints/voteboard/voteboard.ts +++ b/apps/web/src/generated/api/endpoints/voteboard/voteboard.ts @@ -24,12 +24,12 @@ import type { import type { ErrorResponse, GetVotePostsByCursorParams, - VotePostCreateRequest, - VotePostDetailResponse, - VotePostIdResponse, - VotePostListResponse, - VotePostUpdateRequest, VoteRequest, + VoteboardCreateRequest, + VoteboardCreateResponse, + VoteboardCursorResponse, + VoteboardDetailResponse, + VoteboardUpdateRequest, } from '../../models'; import { customInstance } from '../../../../lib/api-client'; @@ -40,18 +40,20 @@ import { customInstance } from '../../../../lib/api-client'; **특징:** - 조회 시 조회수 자동 증가 - 인증/비인증 사용자 모두 조회 가능 -- 인증 여부에 따라 isLiked, canEdit, canDelete 값 변경 +- 인증 여부에 따라 hasVoted, isLiked, canEdit, canDelete 값 변경 - 투표 참여 여부에 따라 selectedOptionIds 값 변경 **인증 사용자:** - isAuthorized: true +- hasVoted: boolean (투표 참여 여부 - 참여했으면 true, 안 했으면 false) - isLiked: boolean (좋아요 상태) - canEdit: boolean (수정 권한 - 작성자인 경우 true) - canDelete: boolean (삭제 권한 - 작성자인 경우 true) -- selectedOptionIds: 투표한 옵션 ID 목록 (투표 전이면 빈 배열) +- selectedOptionIds: 투표한 옵션 ID 목록 (투표 전이면 빈 배열, 투표 후에는 선택한 옵션 ID들) **비인증 사용자:** - isAuthorized: false +- hasVoted: null - isLiked: null - canEdit: null - canDelete: null @@ -63,7 +65,7 @@ export const getVotePost = ( votesboardId: number, signal?: AbortSignal, ) => { - return customInstance({ + return customInstance({ url: `/community/votesboard/${votesboardId}`, method: 'GET', signal, @@ -237,41 +239,41 @@ export function useGetVotePost< */ export const updateVotePost = ( votesboardId: number, - votePostUpdateRequest: VotePostUpdateRequest, + voteboardUpdateRequest: VoteboardUpdateRequest, ) => { const formData = new FormData(); - if (votePostUpdateRequest.category !== undefined) { - formData.append(`category`, votePostUpdateRequest.category); + if (voteboardUpdateRequest.category !== undefined) { + formData.append(`category`, voteboardUpdateRequest.category); } - if (votePostUpdateRequest.title !== undefined) { - formData.append(`title`, votePostUpdateRequest.title); + if (voteboardUpdateRequest.title !== undefined) { + formData.append(`title`, voteboardUpdateRequest.title); } - if (votePostUpdateRequest.content !== undefined) { - formData.append(`content`, votePostUpdateRequest.content); + if (voteboardUpdateRequest.content !== undefined) { + formData.append(`content`, voteboardUpdateRequest.content); } - if (votePostUpdateRequest.images !== undefined) { - votePostUpdateRequest.images.forEach((value) => + if (voteboardUpdateRequest.images !== undefined) { + voteboardUpdateRequest.images.forEach((value) => formData.append(`images`, value), ); } - if (votePostUpdateRequest.deleteImageIds !== undefined) { - votePostUpdateRequest.deleteImageIds.forEach((value) => + if (voteboardUpdateRequest.deleteImageIds !== undefined) { + voteboardUpdateRequest.deleteImageIds.forEach((value) => formData.append(`deleteImageIds`, value.toString()), ); } - if (votePostUpdateRequest.endTime !== undefined) { - formData.append(`endTime`, votePostUpdateRequest.endTime); + if (voteboardUpdateRequest.endTime !== undefined) { + formData.append(`endTime`, voteboardUpdateRequest.endTime); } - if (votePostUpdateRequest.allowRevote !== undefined) { + if (voteboardUpdateRequest.allowRevote !== undefined) { formData.append( `allowRevote`, - votePostUpdateRequest.allowRevote.toString(), + voteboardUpdateRequest.allowRevote.toString(), ); } - if (votePostUpdateRequest.allowMultipleChoice !== undefined) { + if (voteboardUpdateRequest.allowMultipleChoice !== undefined) { formData.append( `allowMultipleChoice`, - votePostUpdateRequest.allowMultipleChoice.toString(), + voteboardUpdateRequest.allowMultipleChoice.toString(), ); } @@ -290,13 +292,13 @@ export const getUpdateVotePostMutationOptions = < mutation?: UseMutationOptions< Awaited>, TError, - { votesboardId: number; data: VotePostUpdateRequest }, + { votesboardId: number; data: VoteboardUpdateRequest }, TContext >; }): UseMutationOptions< Awaited>, TError, - { votesboardId: number; data: VotePostUpdateRequest }, + { votesboardId: number; data: VoteboardUpdateRequest }, TContext > => { const mutationKey = ['updateVotePost']; @@ -310,7 +312,7 @@ export const getUpdateVotePostMutationOptions = < const mutationFn: MutationFunction< Awaited>, - { votesboardId: number; data: VotePostUpdateRequest } + { votesboardId: number; data: VoteboardUpdateRequest } > = (props) => { const { votesboardId, data } = props ?? {}; @@ -323,7 +325,7 @@ export const getUpdateVotePostMutationOptions = < export type UpdateVotePostMutationResult = NonNullable< Awaited> >; -export type UpdateVotePostMutationBody = VotePostUpdateRequest; +export type UpdateVotePostMutationBody = VoteboardUpdateRequest; export type UpdateVotePostMutationError = | ErrorResponse | ErrorResponse; @@ -339,7 +341,7 @@ export const useUpdateVotePost = < mutation?: UseMutationOptions< Awaited>, TError, - { votesboardId: number; data: VotePostUpdateRequest }, + { votesboardId: number; data: VoteboardUpdateRequest }, TContext >; }, @@ -347,7 +349,7 @@ export const useUpdateVotePost = < ): UseMutationResult< Awaited>, TError, - { votesboardId: number; data: VotePostUpdateRequest }, + { votesboardId: number; data: VoteboardUpdateRequest }, TContext > => { const mutationOptions = getUpdateVotePostMutationOptions(options); @@ -729,13 +731,31 @@ export const useCancelVote = < }; /** * 커서 기반 페이지네이션으로 투표 게시글 목록을 조회합니다. + +**정렬 옵션:** +- LATEST: 최신순 (기본값) +- LIKE: 투표순 (투표 인원 많은 순) +- COMMENT: 댓글순 +- VIEW: 조회순 + +**커서 사용법:** +첫 요청: cursor 없이 요청 +다음 페이지: 이전 응답의 nextCursor 값을 사용 + +**응답 필드:** +- contentPreview: 게시글 내용 미리보기 (100자 제한) +- hasVoted: 현재 사용자의 투표 참여 여부 (인증 사용자만, 비인증 시 null) +- isLiked: 현재 사용자의 좋아요 여부 (인증 사용자만, 비인증 시 null) +- totalCount: 전체 게시글 수 (필터 적용된 결과) +- isAuthorized: 요청 사용자의 인증 여부 + * @summary 투표 게시글 목록 조회 (커서 기반) */ export const getVotePostsByCursor = ( params?: GetVotePostsByCursorParams, signal?: AbortSignal, ) => { - return customInstance({ + return customInstance({ url: `/community/votesboard`, method: 'GET', params, @@ -754,7 +774,7 @@ export const getGetVotePostsByCursorQueryKey = ( export const getGetVotePostsByCursorQueryOptions = < TData = Awaited>, - TError = unknown, + TError = ErrorResponse, >( params?: GetVotePostsByCursorParams, options?: { @@ -786,11 +806,11 @@ export const getGetVotePostsByCursorQueryOptions = < export type GetVotePostsByCursorQueryResult = NonNullable< Awaited> >; -export type GetVotePostsByCursorQueryError = unknown; +export type GetVotePostsByCursorQueryError = ErrorResponse; export function useGetVotePostsByCursor< TData = Awaited>, - TError = unknown, + TError = ErrorResponse, >( params: undefined | GetVotePostsByCursorParams, options: { @@ -816,7 +836,7 @@ export function useGetVotePostsByCursor< }; export function useGetVotePostsByCursor< TData = Awaited>, - TError = unknown, + TError = ErrorResponse, >( params?: GetVotePostsByCursorParams, options?: { @@ -842,7 +862,7 @@ export function useGetVotePostsByCursor< }; export function useGetVotePostsByCursor< TData = Awaited>, - TError = unknown, + TError = ErrorResponse, >( params?: GetVotePostsByCursorParams, options?: { @@ -864,7 +884,7 @@ export function useGetVotePostsByCursor< export function useGetVotePostsByCursor< TData = Awaited>, - TError = unknown, + TError = ErrorResponse, >( params?: GetVotePostsByCursorParams, options?: { @@ -913,32 +933,32 @@ export function useGetVotePostsByCursor< * @summary 투표 게시글 작성 */ export const createVotePost = ( - votePostCreateRequest: VotePostCreateRequest, + voteboardCreateRequest: VoteboardCreateRequest, signal?: AbortSignal, ) => { const formData = new FormData(); - formData.append(`category`, votePostCreateRequest.category); - formData.append(`title`, votePostCreateRequest.title); - formData.append(`content`, votePostCreateRequest.content); - votePostCreateRequest.voteOptions.forEach((value) => + formData.append(`category`, voteboardCreateRequest.category); + formData.append(`title`, voteboardCreateRequest.title); + formData.append(`content`, voteboardCreateRequest.content); + voteboardCreateRequest.voteOptions.forEach((value) => formData.append(`voteOptions`, JSON.stringify(value)), ); - formData.append(`endTime`, votePostCreateRequest.endTime); + formData.append(`endTime`, voteboardCreateRequest.endTime); formData.append( `allowRevote`, - votePostCreateRequest.allowRevote.toString(), + voteboardCreateRequest.allowRevote.toString(), ); formData.append( `allowMultipleChoice`, - votePostCreateRequest.allowMultipleChoice.toString(), + voteboardCreateRequest.allowMultipleChoice.toString(), ); - if (votePostCreateRequest.images !== undefined) { - votePostCreateRequest.images.forEach((value) => + if (voteboardCreateRequest.images !== undefined) { + voteboardCreateRequest.images.forEach((value) => formData.append(`images`, value), ); } - return customInstance({ + return customInstance({ url: `/community/votesboard`, method: 'POST', headers: { 'Content-Type': 'multipart/form-data' }, @@ -954,13 +974,13 @@ export const getCreateVotePostMutationOptions = < mutation?: UseMutationOptions< Awaited>, TError, - { data: VotePostCreateRequest }, + { data: VoteboardCreateRequest }, TContext >; }): UseMutationOptions< Awaited>, TError, - { data: VotePostCreateRequest }, + { data: VoteboardCreateRequest }, TContext > => { const mutationKey = ['createVotePost']; @@ -974,7 +994,7 @@ export const getCreateVotePostMutationOptions = < const mutationFn: MutationFunction< Awaited>, - { data: VotePostCreateRequest } + { data: VoteboardCreateRequest } > = (props) => { const { data } = props ?? {}; @@ -987,7 +1007,7 @@ export const getCreateVotePostMutationOptions = < export type CreateVotePostMutationResult = NonNullable< Awaited> >; -export type CreateVotePostMutationBody = VotePostCreateRequest; +export type CreateVotePostMutationBody = VoteboardCreateRequest; export type CreateVotePostMutationError = | ErrorResponse | ErrorResponse @@ -1004,7 +1024,7 @@ export const useCreateVotePost = < mutation?: UseMutationOptions< Awaited>, TError, - { data: VotePostCreateRequest }, + { data: VoteboardCreateRequest }, TContext >; }, @@ -1012,7 +1032,7 @@ export const useCreateVotePost = < ): UseMutationResult< Awaited>, TError, - { data: VotePostCreateRequest }, + { data: VoteboardCreateRequest }, TContext > => { const mutationOptions = getCreateVotePostMutationOptions(options); diff --git a/apps/web/src/generated/api/models/getVotePostsByCursorParams.ts b/apps/web/src/generated/api/models/getVotePostsByCursorParams.ts index 88486e8..651d90e 100644 --- a/apps/web/src/generated/api/models/getVotePostsByCursorParams.ts +++ b/apps/web/src/generated/api/models/getVotePostsByCursorParams.ts @@ -6,6 +6,7 @@ * OpenAPI spec version: v1.0.0 */ import type { GetVotePostsByCursorStatus } from './getVotePostsByCursorStatus'; +import type { GetVotePostsByCursorSort } from './getVotePostsByCursorSort'; export type GetVotePostsByCursorParams = { /** @@ -13,11 +14,19 @@ export type GetVotePostsByCursorParams = { */ status?: GetVotePostsByCursorStatus; /** - * 페이지 크기 + * 투표게시판 정렬 기준 */ - size?: number; + sort?: GetVotePostsByCursorSort; /** - * 커서 (이전 페이지의 마지막 게시글 ID) + * 페이지 크기 (1-50, 기본값: 20) */ + size?: number; + /** + * 커서 기반 페이징을 위한 커서 값 + +**첫 요청:** cursor 없이 요청 +**다음 페이지:** 이전 응답의 nextCursor 값 사용 + + */ cursor?: string; }; diff --git a/apps/web/src/generated/api/models/getVotePostsByCursorSort.ts b/apps/web/src/generated/api/models/getVotePostsByCursorSort.ts new file mode 100644 index 0000000..4c65762 --- /dev/null +++ b/apps/web/src/generated/api/models/getVotePostsByCursorSort.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +export type GetVotePostsByCursorSort = + (typeof GetVotePostsByCursorSort)[keyof typeof GetVotePostsByCursorSort]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const GetVotePostsByCursorSort = { + LATEST: 'LATEST', + LIKE: 'LIKE', + COMMENT: 'COMMENT', + VIEW: 'VIEW', +} as const; diff --git a/apps/web/src/generated/api/models/index.ts b/apps/web/src/generated/api/models/index.ts index 99a93b0..0687d72 100644 --- a/apps/web/src/generated/api/models/index.ts +++ b/apps/web/src/generated/api/models/index.ts @@ -53,6 +53,7 @@ export * from './getPostsByCursorSort'; export * from './getVotePostListParams'; export * from './getVotePostListStatus'; export * from './getVotePostsByCursorParams'; +export * from './getVotePostsByCursorSort'; export * from './getVotePostsByCursorStatus'; export * from './getVoteboardCommentsByCursorParams'; export * from './getVoteboardCommentsByCursorSort'; @@ -110,3 +111,15 @@ export * from './voteboardCommentCreateResponse'; export * from './voteboardCommentCursorResponse'; export * from './voteboardCommentSummary'; export * from './voteboardCommentUpdateRequest'; +export * from './voteboardCreateRequest'; +export * from './voteboardCreateRequestCategory'; +export * from './voteboardCreateResponse'; +export * from './voteboardCursorResponse'; +export * from './voteboardDetailResponse'; +export * from './voteboardDetailResponseCategory'; +export * from './voteboardDetailResponseVoteStatus'; +export * from './voteboardSummary'; +export * from './voteboardSummaryCategory'; +export * from './voteboardSummaryVoteStatus'; +export * from './voteboardUpdateRequest'; +export * from './voteboardUpdateRequestCategory'; diff --git a/apps/web/src/generated/api/models/voteboardCreateRequest.ts b/apps/web/src/generated/api/models/voteboardCreateRequest.ts new file mode 100644 index 0000000..39778c9 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardCreateRequest.ts @@ -0,0 +1,47 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ +import type { VoteboardCreateRequestCategory } from './voteboardCreateRequestCategory'; +import type { VoteOptionRequest } from './voteOptionRequest'; + +/** + * 투표 게시판 게시글 생성 요청 + */ +export interface VoteboardCreateRequest { + /** 게시글 카테고리 */ + category: VoteboardCreateRequestCategory; + /** + * 게시글 제목 + * @minLength 0 + * @maxLength 100 + */ + title: string; + /** + * 게시글 내용 + * @minLength 0 + * @maxLength 5000 + */ + content: string; + /** + * 투표 옵션 목록 (2-5개) + * @minItems 2 + * @maxItems 5 + */ + voteOptions: VoteOptionRequest[]; + /** 투표 마감 시간 */ + endTime: string; + /** 재투표 허용 여부 (투표 후 변경 가능 여부) */ + allowRevote: boolean; + /** 중복 선택 허용 여부 (true: 여러 옵션 동시 선택 가능, 최대 n-1개 / false: 하나의 옵션만 선택 가능) */ + allowMultipleChoice: boolean; + /** + * 첨부 이미지 파일들 (최대 4장) + * @minItems 0 + * @maxItems 4 + */ + images?: Blob[]; +} diff --git a/apps/web/src/generated/api/models/voteboardCreateRequestCategory.ts b/apps/web/src/generated/api/models/voteboardCreateRequestCategory.ts new file mode 100644 index 0000000..cfc91bc --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardCreateRequestCategory.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 게시글 카테고리 + */ +export type VoteboardCreateRequestCategory = + (typeof VoteboardCreateRequestCategory)[keyof typeof VoteboardCreateRequestCategory]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const VoteboardCreateRequestCategory = { + 'daily-hobby': 'daily-hobby', + restaurant: 'restaurant', + 'living-convenience': 'living-convenience', + 'neighborhood-news': 'neighborhood-news', + startup: 'startup', + others: 'others', +} as const; diff --git a/apps/web/src/generated/api/models/voteboardCreateResponse.ts b/apps/web/src/generated/api/models/voteboardCreateResponse.ts new file mode 100644 index 0000000..d86f998 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardCreateResponse.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 투표 게시판 게시글 생성 응답 + */ +export interface VoteboardCreateResponse { + /** 생성된 투표 게시글 ID */ + postId: number; +} diff --git a/apps/web/src/generated/api/models/voteboardCursorResponse.ts b/apps/web/src/generated/api/models/voteboardCursorResponse.ts new file mode 100644 index 0000000..b272d3d --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardCursorResponse.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ +import type { VoteboardSummary } from './voteboardSummary'; + +/** + * 투표 게시판 커서 기반 목록 조회 응답 + */ +export interface VoteboardCursorResponse { + /** 투표 게시글 목록 */ + posts: VoteboardSummary[]; + /** 다음 페이지 존재 여부 */ + hasNext: boolean; + /** 다음 페이지를 위한 커서 값 */ + nextCursor?: string; + /** 현재 페이지 크기 */ + size: number; + /** 총 게시글 수 */ + totalCount: number; + authorized?: boolean; + /** 요청한 사용자가 인증되었는지 여부 (액세스 토큰 제공 여부) */ + isAuthorized: boolean; +} diff --git a/apps/web/src/generated/api/models/voteboardDetailResponse.ts b/apps/web/src/generated/api/models/voteboardDetailResponse.ts new file mode 100644 index 0000000..a5d03d6 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardDetailResponse.ts @@ -0,0 +1,63 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ +import type { UserSummaryResponse } from './userSummaryResponse'; +import type { VoteboardDetailResponseCategory } from './voteboardDetailResponseCategory'; +import type { ImageInfo } from './imageInfo'; +import type { VoteOptionResponse } from './voteOptionResponse'; +import type { VoteboardDetailResponseVoteStatus } from './voteboardDetailResponseVoteStatus'; + +/** + * 투표 게시판 게시글 상세 정보 + */ +export interface VoteboardDetailResponse { + /** 게시글 ID */ + postId: number; + /** 작성자 정보 */ + author: UserSummaryResponse; + /** 카테고리 */ + category: VoteboardDetailResponseCategory; + /** 게시글 제목 */ + title: string; + /** 게시글 내용 */ + content: string; + /** 첨부된 이미지 정보 목록 */ + images: ImageInfo[]; + /** 투표 옵션 목록 */ + voteOptions: VoteOptionResponse[]; + /** 현재 사용자의 투표 참여 여부 (비인증 사용자인 경우 null, 참여하지 않은 경우 false, 참여한 경우 true) */ + hasVoted: boolean; + /** 현재 사용자가 선택한 옵션 ID 목록 (미투표 시 빈 리스트) */ + selectedOptionIds?: number[]; + /** 총 투표 참여자 수 */ + totalVotes: number; + /** 투표 상태 (IN_PROGRESS: 진행중, COMPLETED: 완료) */ + voteStatus: VoteboardDetailResponseVoteStatus; + /** 투표 마감 시간 */ + endTime: string; + /** 재투표 허용 여부 (투표 후 변경 가능 여부) */ + allowRevote: boolean; + /** 중복 선택 허용 여부 (여러 옵션 동시 선택 가능 여부) */ + allowMultipleChoice: boolean; + /** 조회수 */ + viewCount: number; + /** 댓글 수 */ + commentCount: number; + /** 좋아요 수 */ + likeCount: number; + /** 현재 사용자가 게시글 수정 권한이 있는지 여부 (비인증 사용자인 경우 null, 작성자인 경우 true) */ + canEdit: boolean; + /** 현재 사용자가 게시글 삭제 권한이 있는지 여부 (비인증 사용자인 경우 null, 작성자인 경우 true) */ + canDelete: boolean; + /** 생성일시 */ + createdDate: string; + /** 수정일시 */ + lastModifiedDate: string; + authorized?: boolean; + /** 현재 사용자의 좋아요 여부 (비인증 사용자인 경우 null) */ + isLiked: boolean; +} diff --git a/apps/web/src/generated/api/models/voteboardDetailResponseCategory.ts b/apps/web/src/generated/api/models/voteboardDetailResponseCategory.ts new file mode 100644 index 0000000..c0e5c13 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardDetailResponseCategory.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 카테고리 + */ +export type VoteboardDetailResponseCategory = + (typeof VoteboardDetailResponseCategory)[keyof typeof VoteboardDetailResponseCategory]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const VoteboardDetailResponseCategory = { + 'daily-hobby': 'daily-hobby', + restaurant: 'restaurant', + 'living-convenience': 'living-convenience', + 'neighborhood-news': 'neighborhood-news', + startup: 'startup', + others: 'others', +} as const; diff --git a/apps/web/src/generated/api/models/voteboardDetailResponseVoteStatus.ts b/apps/web/src/generated/api/models/voteboardDetailResponseVoteStatus.ts new file mode 100644 index 0000000..db9e689 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardDetailResponseVoteStatus.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 투표 상태 (IN_PROGRESS: 진행중, COMPLETED: 완료) + */ +export type VoteboardDetailResponseVoteStatus = + (typeof VoteboardDetailResponseVoteStatus)[keyof typeof VoteboardDetailResponseVoteStatus]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const VoteboardDetailResponseVoteStatus = { + IN_PROGRESS: 'IN_PROGRESS', + COMPLETED: 'COMPLETED', + DELETED: 'DELETED', +} as const; diff --git a/apps/web/src/generated/api/models/voteboardSummary.ts b/apps/web/src/generated/api/models/voteboardSummary.ts new file mode 100644 index 0000000..1bd4d7f --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardSummary.ts @@ -0,0 +1,57 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ +import type { UserSummaryResponse } from './userSummaryResponse'; +import type { VoteboardSummaryCategory } from './voteboardSummaryCategory'; +import type { VoteboardSummaryVoteStatus } from './voteboardSummaryVoteStatus'; +import type { VoteOptionResponse } from './voteOptionResponse'; + +/** + * 투표 게시판 게시글 요약 정보 + */ +export interface VoteboardSummary { + /** 게시글 ID */ + postId: number; + /** 작성자 정보 */ + author: UserSummaryResponse; + /** 카테고리 */ + category: VoteboardSummaryCategory; + /** 게시글 제목 */ + title: string; + /** 내용 미리보기 (100자 제한) */ + contentPreview: string; + /** 첫 번째 이미지 URL (썸네일용) */ + thumbnailUrl?: string; + /** 이미지 개수 */ + imageCount: number; + /** 조회수 */ + viewCount: number; + /** 댓글 수 */ + commentCount: number; + /** 총 투표 참여자 수 */ + totalVotes: number; + /** 현재 사용자의 투표 참여 여부 (비인증 사용자인 경우 null, 참여하지 않은 경우 false, 참여한 경우 true) */ + hasVoted: boolean; + /** 투표 상태 (IN_PROGRESS: 진행중, COMPLETED: 완료) */ + voteStatus: VoteboardSummaryVoteStatus; + /** 투표 마감 시간 */ + endTime: string; + /** 재투표 허용 여부 (투표 후 변경 가능 여부) */ + allowRevote: boolean; + /** 중복 선택 허용 여부 (여러 옵션 동시 선택 가능 여부) */ + allowMultipleChoice: boolean; + /** 투표 옵션 목록 (미리보기, 최대 3개) */ + voteOptions: VoteOptionResponse[]; + /** 좋아요 수 */ + likeCount: number; + /** 생성일시 */ + createdDate: string; + /** 수정일시 */ + lastModifiedDate: string; + /** 현재 사용자의 좋아요 여부 (비인증 사용자인 경우 null) */ + isLiked: boolean; +} diff --git a/apps/web/src/generated/api/models/voteboardSummaryCategory.ts b/apps/web/src/generated/api/models/voteboardSummaryCategory.ts new file mode 100644 index 0000000..322b2d7 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardSummaryCategory.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 카테고리 + */ +export type VoteboardSummaryCategory = + (typeof VoteboardSummaryCategory)[keyof typeof VoteboardSummaryCategory]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const VoteboardSummaryCategory = { + 'daily-hobby': 'daily-hobby', + restaurant: 'restaurant', + 'living-convenience': 'living-convenience', + 'neighborhood-news': 'neighborhood-news', + startup: 'startup', + others: 'others', +} as const; diff --git a/apps/web/src/generated/api/models/voteboardSummaryVoteStatus.ts b/apps/web/src/generated/api/models/voteboardSummaryVoteStatus.ts new file mode 100644 index 0000000..8521d27 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardSummaryVoteStatus.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 투표 상태 (IN_PROGRESS: 진행중, COMPLETED: 완료) + */ +export type VoteboardSummaryVoteStatus = + (typeof VoteboardSummaryVoteStatus)[keyof typeof VoteboardSummaryVoteStatus]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const VoteboardSummaryVoteStatus = { + IN_PROGRESS: 'IN_PROGRESS', + COMPLETED: 'COMPLETED', + DELETED: 'DELETED', +} as const; diff --git a/apps/web/src/generated/api/models/voteboardUpdateRequest.ts b/apps/web/src/generated/api/models/voteboardUpdateRequest.ts new file mode 100644 index 0000000..68524a0 --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardUpdateRequest.ts @@ -0,0 +1,42 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ +import type { VoteboardUpdateRequestCategory } from './voteboardUpdateRequestCategory'; + +/** + * 투표 게시판 게시글 수정 요청 (투표 옵션 수정 불가) + */ +export interface VoteboardUpdateRequest { + /** 수정할 카테고리 */ + category?: VoteboardUpdateRequestCategory; + /** + * 게시글 제목 + * @minLength 0 + * @maxLength 100 + */ + title?: string; + /** + * 게시글 내용 + * @minLength 0 + * @maxLength 5000 + */ + content?: string; + /** + * 새로운 이미지 파일들 (기존 이미지 대체, 최대 4장) + * @minItems 0 + * @maxItems 4 + */ + images?: Blob[]; + /** 삭제할 기존 이미지 ID 목록 */ + deleteImageIds?: number[]; + /** 투표 마감 시간 */ + endTime?: string; + /** 재투표 허용 여부 (투표 후 변경 가능 여부) */ + allowRevote?: boolean; + /** 중복 선택 허용 여부 (여러 옵션 동시 선택 가능 여부) */ + allowMultipleChoice?: boolean; +} diff --git a/apps/web/src/generated/api/models/voteboardUpdateRequestCategory.ts b/apps/web/src/generated/api/models/voteboardUpdateRequestCategory.ts new file mode 100644 index 0000000..42477fb --- /dev/null +++ b/apps/web/src/generated/api/models/voteboardUpdateRequestCategory.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.13.0 🍺 + * Do not edit manually. + * SoSo API 명세서 + * 소소한 아이디어 공유 플랫폼의 백엔드 API 문서입니다. + * OpenAPI spec version: v1.0.0 + */ + +/** + * 수정할 카테고리 + */ +export type VoteboardUpdateRequestCategory = + (typeof VoteboardUpdateRequestCategory)[keyof typeof VoteboardUpdateRequestCategory]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const VoteboardUpdateRequestCategory = { + 'daily-hobby': 'daily-hobby', + restaurant: 'restaurant', + 'living-convenience': 'living-convenience', + 'neighborhood-news': 'neighborhood-news', + startup: 'startup', + others: 'others', +} as const;