diff --git a/products/backend/src/db/pay-crew2-schema.ts b/products/backend/src/db/pay-crew2-schema.ts index 01f7ae0..7c09e0a 100644 --- a/products/backend/src/db/pay-crew2-schema.ts +++ b/products/backend/src/db/pay-crew2-schema.ts @@ -1,5 +1,5 @@ // drizzle -import { pgTable, text, timestamp, index, uuid, uniqueIndex, integer } from 'drizzle-orm/pg-core'; +import { pgTable, text, timestamp, index, uuid, uniqueIndex, integer, date } from 'drizzle-orm/pg-core'; //auth-schema import { user } from './auth-schema'; @@ -8,7 +8,7 @@ export const group = pgTable( { id: uuid('id').primaryKey(), name: text('name').notNull(), - invite_id: text('invite_id').notNull().unique(), + inviteId: text('invite_id').notNull().unique(), createdBy: text('created_by') .references(() => user.id, { onDelete: 'set null' }) .notNull(), @@ -58,7 +58,7 @@ export const debt = pgTable( .references(() => user.id, { onDelete: 'cascade' }), amount: integer('amount').notNull(), description: text('description'), - occurredAt: timestamp('occurred_at').defaultNow().notNull(), + occurredAt: date('occurred_at').notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at') .defaultNow() diff --git a/products/backend/src/presentation/routes/group.ts b/products/backend/src/presentation/routes/group.ts index 99f4209..fd4aa45 100644 --- a/products/backend/src/presentation/routes/group.ts +++ b/products/backend/src/presentation/routes/group.ts @@ -18,8 +18,6 @@ import { type GetGroupDebtHistoryResponseSchemaType, GetGroupInfoResponseMemberElementSchemaType, registerGroupDebtRequestSchema, - registerGroupDebtResponseSchema, - type RegisterGroupDebtResponseSchemaType, deleteGroupDebtRequestSchema, } from 'validator'; // error schema @@ -67,9 +65,6 @@ hono.openapi(createGroupSchema, async (c) => { const loginUser = c.get('user'); const body = c.req.valid('json'); - // 現在時刻の取得 - const now = new Date(); - // データベース接続 const db = drizzle({ connection: c.env.HYPERDRIVE }); @@ -79,14 +74,12 @@ hono.openapi(createGroupSchema, async (c) => { .values({ id: crypto.randomUUID(), name: body.group_name, - invite_id: `${crypto.randomUUID()}-${crypto.randomUUID()}`, + inviteId: `${crypto.randomUUID()}-${crypto.randomUUID()}`, createdBy: loginUser.id, - createdAt: now, - updatedAt: now, }) .returning({ id: group.id, - invite_id: group.invite_id, + inviteId: group.inviteId, }); //* グループ作成者をgroupMembershipに挿入 *// @@ -94,14 +87,13 @@ hono.openapi(createGroupSchema, async (c) => { id: crypto.randomUUID(), groupId: result[0].id, userId: loginUser.id, - joinedAt: now, }); // レスポンス return c.json( { group_id: result[0].id, - invite_id: result[0].invite_id, + invite_id: result[0].inviteId, } satisfies CreateGroupResponseSchemaType, 201 ); @@ -142,9 +134,6 @@ hono.openapi(joinGroupSchema, async (c) => { const body = c.req.valid('json'); const user = c.get('user'); - // 現在時刻の取得 - const now = new Date(); - // データベース接続 const db = drizzle({ connection: c.env.HYPERDRIVE }); @@ -154,7 +143,7 @@ hono.openapi(joinGroupSchema, async (c) => { id: group.id, }) .from(group) - .where(eq(group.invite_id, body.invite_id)) + .where(eq(group.inviteId, body.invite_id)) .limit(1); // invite_id が不正な場合はエラー @@ -188,7 +177,6 @@ hono.openapi(joinGroupSchema, async (c) => { id: crypto.randomUUID(), groupId: groupData[0].id, userId: user.id, - joinedAt: now, }) .returning({ groupId: groupMembership.groupId, @@ -261,6 +249,7 @@ hono.openapi(getGroupInfoSchema, async (c) => { const groupData = await db .select({ name: group.name, + inviteId: group.inviteId, createdBy: group.createdBy, }) .from(group) @@ -320,7 +309,9 @@ hono.openapi(getGroupInfoSchema, async (c) => { return c.json( { group_name: groupData[0].name, - created_by: + invite_id: groupData[0].inviteId, + created_by_id: groupData[0].createdBy, + created_by_name: createdByUserNameInfo[0].displayName !== null && createdByUserNameInfo[0].displayName.length > 0 ? createdByUserNameInfo[0].displayName : createdByUserNameInfo[0].name, @@ -395,6 +386,8 @@ hono.openapi(getGroupDebtHistorySchema, async (c) => { debtorId: debt.debtorId, creditorId: debt.creditorId, amount: debt.amount, + description: debt.description, + occurredAt: debt.occurredAt, }) .from(debt) .where(and(eq(debt.groupId, body.group_id), isNull(debt.deletedAt))); @@ -438,6 +431,8 @@ hono.openapi(getGroupDebtHistorySchema, async (c) => { ? CreditorNameInfo[0].displayName : CreditorNameInfo[0].name, amount: debtEntry.amount, + description: debtEntry.description === null ? '' : debtEntry.description, + occurred_at: debtEntry.occurredAt, }); } @@ -468,13 +463,8 @@ const registerGroupDebtSchema = route.createSchema( }, }, responses: { - 201: { - description: 'Created', - content: { - 'application/json': { - schema: registerGroupDebtResponseSchema, - }, - }, + 204: { + description: 'No Content', }, }, }, @@ -485,9 +475,6 @@ hono.openapi(registerGroupDebtSchema, async (c) => { const loginUser = c.get('user'); const body = c.req.valid('json'); - // 現在時刻の取得 - const now = new Date(); - // データベース接続 const db = drizzle({ connection: c.env.HYPERDRIVE }); @@ -508,35 +495,17 @@ hono.openapi(registerGroupDebtSchema, async (c) => { // NOTE: --- 共通化終了 --- // body.group_id に貸し借り履歴を追加 (debt table) - const result = await db - .insert(debt) - .values({ - id: crypto.randomUUID(), - groupId: body.group_id, - creditorId: body.creditor_id, - debtorId: body.debtor_id, - amount: body.amount, - description: typeof body.description === 'undefined' ? null : body.description, - occurredAt: body.occurred_at ? new Date(body.occurred_at) : now, - createdAt: now, - updatedAt: now, - }) - .returning({ - creditorId: debt.creditorId, - debtorId: debt.debtorId, - amount: debt.amount, - occurredAt: debt.occurredAt, - }); + await db.insert(debt).values({ + id: crypto.randomUUID(), + groupId: body.group_id, + creditorId: body.creditor_id, + debtorId: body.debtor_id, + amount: body.amount, + description: typeof body.description === 'undefined' ? null : body.description, + occurredAt: body.occurred_at, + }); - return c.json( - { - creditorId: result[0].creditorId, - debtorId: result[0].debtorId, - amount: result[0].amount, - occurredAt: result[0].occurredAt, - } satisfies RegisterGroupDebtResponseSchemaType, - 201 - ); + return c.body(null, 204); }); // TODO: 貸し借りの履歴の削除エンドポイントの登録 @@ -597,7 +566,6 @@ hono.openapi(deleteGroupDebtSchema, async (c) => { .set({ deletedBy: loginUser.id, deletedAt: now, - updatedAt: now, }) .where(and(eq(debt.id, body.debt_id), eq(debt.groupId, body.group_id), isNull(debt.deletedAt))); diff --git a/products/backend/src/presentation/routes/info.ts b/products/backend/src/presentation/routes/info.ts index 6a7452c..0417912 100644 --- a/products/backend/src/presentation/routes/info.ts +++ b/products/backend/src/presentation/routes/info.ts @@ -298,7 +298,6 @@ hono.openapi(infoUserRepaymentSchema, async (c) => { .set({ deletedBy: loginUser.id, deletedAt: now, - updatedAt: now, }) .where( or( diff --git a/products/frontend/openapi.json b/products/frontend/openapi.json index f8b95d0..f4883c1 100644 --- a/products/frontend/openapi.json +++ b/products/frontend/openapi.json @@ -4591,7 +4591,15 @@ "type": "string", "format": "uuid" }, - "created_by": { + "invite_id": { + "type": "string", + "minLength": 1 + }, + "created_by_id": { + "type": "string", + "minLength": 1 + }, + "created_by_name": { "type": "string", "minLength": 1 }, @@ -4618,7 +4626,9 @@ }, "required": [ "group_name", - "created_by", + "invite_id", + "created_by_id", + "created_by_name", "members" ] } @@ -5855,6 +5865,14 @@ "amount": { "type": "number", "minimum": 0 + }, + "description": { + "type": "string", + "minLength": 0 + }, + "occurred_at": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" } }, "required": [ @@ -5863,7 +5881,9 @@ "debtor_name", "creditor_id", "creditor_name", - "amount" + "amount", + "description", + "occurred_at" ] } } @@ -7077,53 +7097,24 @@ "type": "string" }, "occurred_at": { - "type": "string" + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" } }, "required": [ "group_id", "creditor_id", "debtor_id", - "amount" + "amount", + "occurred_at" ] } } } }, "responses": { - "201": { - "description": "Created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "creditorId": { - "type": "string", - "minLength": 1 - }, - "debtorId": { - "type": "string", - "minLength": 1 - }, - "amount": { - "type": "number", - "minimum": 0 - }, - "occurredAt": { - "type": "string", - "format": "date" - } - }, - "required": [ - "creditorId", - "debtorId", - "amount", - "occurredAt" - ] - } - } - } + "204": { + "description": "No Content" }, "400": { "description": "Bad Request", diff --git a/products/frontend/src/api/openapi.d.ts b/products/frontend/src/api/openapi.d.ts index 8993b57..50ae9b4 100644 --- a/products/frontend/src/api/openapi.d.ts +++ b/products/frontend/src/api/openapi.d.ts @@ -463,7 +463,9 @@ export interface paths { "application/json": { /** Format: uuid */ group_name: string; - created_by: string; + invite_id: string; + created_by_id: string; + created_by_name: string; members: { user_id: string; user_name: string; @@ -578,6 +580,8 @@ export interface paths { creditor_id: string; creditor_name: string; amount: number; + description: string; + occurred_at: string; }[]; }; }; @@ -675,25 +679,17 @@ export interface paths { debtor_id: string; amount: number; description?: string; - occurred_at?: string; + occurred_at: string; }; }; }; responses: { - /** @description Created */ - 201: { + /** @description No Content */ + 204: { headers: { [name: string]: unknown; }; - content: { - "application/json": { - creditorId: string; - debtorId: string; - amount: number; - /** Format: date */ - occurredAt: string; - }; - }; + content?: never; }; /** @description Bad Request */ 400: { diff --git a/products/frontend/src/routes/GenerateGroup/index.tsx b/products/frontend/src/routes/GenerateGroup/index.tsx index 8da7cc9..d3af381 100644 --- a/products/frontend/src/routes/GenerateGroup/index.tsx +++ b/products/frontend/src/routes/GenerateGroup/index.tsx @@ -80,7 +80,7 @@ const GenerateGroup: FC = () => { : isError ? `グループの作成に失敗しました: ${error.message}` : isSuccess - ? 'グループの作成しました' + ? 'グループの作成に成功しました' : null}
diff --git a/products/frontend/src/routes/GroupDetail/index.tsx b/products/frontend/src/routes/GroupDetail/index.tsx index d329ea7..b735348 100644 --- a/products/frontend/src/routes/GroupDetail/index.tsx +++ b/products/frontend/src/routes/GroupDetail/index.tsx @@ -16,16 +16,32 @@ import { useForm, type SubmitHandler } from 'react-hook-form'; const GroupDetail: FC = () => { // URLパラメータからgroupIdを取得 const { groupId } = useParams<{ groupId: string }>(); - // groupIdが存在しない場合の処理 if (!groupId) returnGroup ID is not provided.
; + // コピー状態管理 + const [copyStatus, setCopyStatus] = useState<'idle' | 'copying' | 'success' | 'error'>('idle'); + const [inviteUrl, setInviteUrl] = useStateグループ名: {groupInfoResult.group_name}
-作成者: {groupInfoResult.created_by}
+作成者: {groupInfoResult.created_by_name}
+ {inviteUrl && ( +コピーしました
} + {copyStatus === 'error' &&コピーに失敗しました
} +{debtRegisterMutation.isPending - ? 'グループの作成中...' + ? '貸し借りの登録中...' : debtRegisterMutation.isError - ? `グループの作成に失敗しました: ${debtRegisterMutation.error.message}` + ? `貸し借りの登録に失敗しました: ${debtRegisterMutation.error.message}` : debtRegisterMutation.isSuccess - ? 'グループの作成しました' + ? '貸し借りの登録に成功しました' : null}
@@ -225,7 +234,8 @@ const GroupDetail: FC = () => {