mirror of
https://github.com/hex248/sprint.git
synced 2026-02-07 18:23:03 +00:00
656 lines
22 KiB
TypeScript
656 lines
22 KiB
TypeScript
import { z } from "zod";
|
|
import {
|
|
ISSUE_COMMENT_MAX_LENGTH,
|
|
ISSUE_DESCRIPTION_MAX_LENGTH,
|
|
ISSUE_STATUS_MAX_LENGTH,
|
|
ISSUE_TITLE_MAX_LENGTH,
|
|
ISSUE_TYPE_MAX_LENGTH,
|
|
ORG_DESCRIPTION_MAX_LENGTH,
|
|
ORG_NAME_MAX_LENGTH,
|
|
ORG_SLUG_MAX_LENGTH,
|
|
PROJECT_NAME_MAX_LENGTH,
|
|
USER_NAME_MAX_LENGTH,
|
|
USER_USERNAME_MAX_LENGTH,
|
|
} from "./constants";
|
|
import { DEFAULT_FEATURES } from "./schema";
|
|
|
|
// error response
|
|
|
|
export const ApiErrorSchema = z.object({
|
|
error: z.string(),
|
|
code: z.string().optional(),
|
|
details: z.record(z.array(z.string())).optional(),
|
|
});
|
|
|
|
export type ApiError = z.infer<typeof ApiErrorSchema>;
|
|
|
|
// auth schemas
|
|
|
|
export const LoginRequestSchema = z.object({
|
|
username: z.string().min(1, "Username is required").max(USER_USERNAME_MAX_LENGTH),
|
|
password: z.string().min(1, "Password is required"),
|
|
});
|
|
|
|
export type LoginRequest = z.infer<typeof LoginRequestSchema>;
|
|
|
|
export const RegisterRequestSchema = z.object({
|
|
name: z.string().min(1, "Name is required").max(USER_NAME_MAX_LENGTH),
|
|
username: z
|
|
.string()
|
|
.min(1, "Username is required")
|
|
.max(USER_USERNAME_MAX_LENGTH)
|
|
.regex(/^[a-zA-Z0-9_-]+$/, "Username can only contain letters, numbers, underscores, and hyphens"),
|
|
password: z
|
|
.string()
|
|
.min(8, "Password must be at least 8 characters")
|
|
.regex(/[A-Z]/, "Password must contain an uppercase letter")
|
|
.regex(/[a-z]/, "Password must contain a lowercase letter")
|
|
.regex(/[0-9]/, "Password must contain a number"),
|
|
avatarURL: z.string().url().nullable().optional(),
|
|
});
|
|
|
|
export type RegisterRequest = z.infer<typeof RegisterRequestSchema>;
|
|
|
|
export const AuthResponseSchema = z.object({
|
|
user: z.object({
|
|
id: z.number(),
|
|
name: z.string(),
|
|
username: z.string(),
|
|
avatarURL: z.string().nullable(),
|
|
iconPreference: z.enum(["lucide", "pixel", "phosphor"]),
|
|
}),
|
|
csrfToken: z.string(),
|
|
});
|
|
|
|
export type AuthResponse = z.infer<typeof AuthResponseSchema>;
|
|
|
|
// issue schemas
|
|
|
|
export const IssueCreateRequestSchema = z.object({
|
|
projectId: z.number().int().positive("projectId must be a positive integer"),
|
|
type: z.string().max(ISSUE_TYPE_MAX_LENGTH).optional(),
|
|
status: z.string().max(ISSUE_STATUS_MAX_LENGTH).optional(),
|
|
title: z.string().min(1, "Title is required").max(ISSUE_TITLE_MAX_LENGTH),
|
|
description: z.string().max(ISSUE_DESCRIPTION_MAX_LENGTH).default(""),
|
|
assigneeIds: z.array(z.number().int().positive()).optional(),
|
|
sprintId: z.number().int().positive().nullable().optional(),
|
|
});
|
|
|
|
export type IssueCreateRequest = z.infer<typeof IssueCreateRequestSchema>;
|
|
|
|
export const IssueUpdateRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
type: z.string().max(ISSUE_TYPE_MAX_LENGTH).optional(),
|
|
status: z.string().max(ISSUE_STATUS_MAX_LENGTH).optional(),
|
|
title: z.string().min(1, "Title must be at least 1 character").max(ISSUE_TITLE_MAX_LENGTH).optional(),
|
|
description: z.string().max(ISSUE_DESCRIPTION_MAX_LENGTH).optional(),
|
|
assigneeIds: z.array(z.number().int().positive()).nullable().optional(),
|
|
sprintId: z.number().int().positive().nullable().optional(),
|
|
});
|
|
|
|
export type IssueUpdateRequest = z.infer<typeof IssueUpdateRequestSchema>;
|
|
|
|
export const IssueDeleteRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type IssueDeleteRequest = z.infer<typeof IssueDeleteRequestSchema>;
|
|
|
|
export const IssuesByProjectQuerySchema = z.object({
|
|
projectId: z.coerce.number().int().positive("projectId must be a positive integer"),
|
|
});
|
|
|
|
export type IssuesByProjectQuery = z.infer<typeof IssuesByProjectQuerySchema>;
|
|
|
|
export const IssueByIdQuerySchema = z.object({
|
|
issueId: z.coerce.number().int().positive("issueId must be a positive integer"),
|
|
});
|
|
|
|
export type IssueByIdQuery = z.infer<typeof IssueByIdQuerySchema>;
|
|
|
|
export const IssuesStatusCountQuerySchema = z.object({
|
|
organisationId: z.coerce.number().int().positive("organisationId must be a positive integer"),
|
|
status: z.string().min(1, "Status is required").max(ISSUE_STATUS_MAX_LENGTH),
|
|
});
|
|
|
|
export type IssuesStatusCountQuery = z.infer<typeof IssuesStatusCountQuerySchema>;
|
|
|
|
export const IssuesReplaceStatusRequestSchema = z.object({
|
|
organisationId: z.number().int().positive("organisationId must be a positive integer"),
|
|
oldStatus: z.string().min(1, "oldStatus is required").max(ISSUE_STATUS_MAX_LENGTH),
|
|
newStatus: z.string().min(1, "newStatus is required").max(ISSUE_STATUS_MAX_LENGTH),
|
|
});
|
|
|
|
export type IssuesReplaceStatusRequest = z.infer<typeof IssuesReplaceStatusRequestSchema>;
|
|
|
|
export const IssuesTypeCountQuerySchema = z.object({
|
|
organisationId: z.coerce.number().int().positive("organisationId must be a positive integer"),
|
|
type: z.string().min(1, "Type is required").max(ISSUE_TYPE_MAX_LENGTH),
|
|
});
|
|
|
|
export type IssuesTypeCountQuery = z.infer<typeof IssuesTypeCountQuerySchema>;
|
|
|
|
export const IssuesReplaceTypeRequestSchema = z.object({
|
|
organisationId: z.number().int().positive("organisationId must be a positive integer"),
|
|
oldType: z.string().min(1, "oldType is required").max(ISSUE_TYPE_MAX_LENGTH),
|
|
newType: z.string().min(1, "newType is required").max(ISSUE_TYPE_MAX_LENGTH),
|
|
});
|
|
|
|
export type IssuesReplaceTypeRequest = z.infer<typeof IssuesReplaceTypeRequestSchema>;
|
|
|
|
export const IssueCommentCreateRequestSchema = z.object({
|
|
issueId: z.number().int().positive("issueId must be a positive integer"),
|
|
body: z.string().min(1, "Comment is required").max(ISSUE_COMMENT_MAX_LENGTH),
|
|
});
|
|
|
|
export type IssueCommentCreateRequest = z.infer<typeof IssueCommentCreateRequestSchema>;
|
|
|
|
export const IssueCommentDeleteRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type IssueCommentDeleteRequest = z.infer<typeof IssueCommentDeleteRequestSchema>;
|
|
|
|
export const IssueCommentsByIssueQuerySchema = z.object({
|
|
issueId: z.coerce.number().int().positive("issueId must be a positive integer"),
|
|
});
|
|
|
|
export type IssueCommentsByIssueQuery = z.infer<typeof IssueCommentsByIssueQuerySchema>;
|
|
|
|
// organisation schemas
|
|
|
|
export const OrgCreateRequestSchema = z.object({
|
|
name: z.string().min(1, "Name is required").max(ORG_NAME_MAX_LENGTH),
|
|
slug: z
|
|
.string()
|
|
.min(1, "Slug is required")
|
|
.max(ORG_SLUG_MAX_LENGTH)
|
|
.regex(/^[a-z0-9-]+$/, "Slug can only contain lowercase letters, numbers, and hyphens"),
|
|
description: z.string().max(ORG_DESCRIPTION_MAX_LENGTH).optional(),
|
|
});
|
|
|
|
export type OrgCreateRequest = z.infer<typeof OrgCreateRequestSchema>;
|
|
|
|
export const OrgUpdateRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
name: z.string().min(1, "Name must be at least 1 character").max(ORG_NAME_MAX_LENGTH).optional(),
|
|
description: z.string().max(ORG_DESCRIPTION_MAX_LENGTH).optional(),
|
|
slug: z
|
|
.string()
|
|
.min(1, "Slug must be at least 1 character")
|
|
.max(ORG_SLUG_MAX_LENGTH)
|
|
.regex(/^[a-z0-9-]+$/)
|
|
.optional(),
|
|
iconURL: z.string().url().max(512).nullable().optional(),
|
|
statuses: z
|
|
.record(z.string())
|
|
.refine((obj) => Object.keys(obj).length > 0, "Statuses must have at least one entry")
|
|
.refine(
|
|
(obj) => Object.keys(obj).every((key) => key.length <= ISSUE_STATUS_MAX_LENGTH),
|
|
`Status keys must be <= ${ISSUE_STATUS_MAX_LENGTH} characters`,
|
|
)
|
|
.optional(),
|
|
features: z
|
|
.record(z.boolean())
|
|
.refine(
|
|
(obj) => Object.keys(obj).length === Object.keys(DEFAULT_FEATURES).length,
|
|
"Features must include all default features",
|
|
)
|
|
.optional(),
|
|
issueTypes: z
|
|
.record(z.object({ icon: z.string(), color: z.string() }))
|
|
.refine((obj) => Object.keys(obj).length > 0, "Issue types must have at least one entry")
|
|
.refine(
|
|
(obj) => Object.keys(obj).every((key) => key.length <= ISSUE_TYPE_MAX_LENGTH),
|
|
`Issue type keys must be <= ${ISSUE_TYPE_MAX_LENGTH} characters`,
|
|
)
|
|
.optional(),
|
|
});
|
|
|
|
export type OrgUpdateRequest = z.infer<typeof OrgUpdateRequestSchema>;
|
|
|
|
export const OrgDeleteRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type OrgDeleteRequest = z.infer<typeof OrgDeleteRequestSchema>;
|
|
|
|
export const OrgByIdQuerySchema = z.object({
|
|
id: z.coerce.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type OrgByIdQuery = z.infer<typeof OrgByIdQuerySchema>;
|
|
|
|
export const OrgMembersQuerySchema = z.object({
|
|
organisationId: z.coerce.number().int().positive("organisationId must be a positive integer"),
|
|
});
|
|
|
|
export type OrgMembersQuery = z.infer<typeof OrgMembersQuerySchema>;
|
|
|
|
export const OrgMemberTimeTrackingQuerySchema = z.object({
|
|
organisationId: z.coerce.number().int().positive("organisationId must be a positive integer"),
|
|
fromDate: z.coerce.date().optional(),
|
|
});
|
|
|
|
export type OrgMemberTimeTrackingQuery = z.infer<typeof OrgMemberTimeTrackingQuerySchema>;
|
|
|
|
export const OrgAddMemberRequestSchema = z.object({
|
|
organisationId: z.number().int().positive("organisationId must be a positive integer"),
|
|
userId: z.number().int().positive("userId must be a positive integer"),
|
|
role: z.enum(["admin", "member"]).default("member"),
|
|
});
|
|
|
|
export type OrgAddMemberRequest = z.infer<typeof OrgAddMemberRequestSchema>;
|
|
|
|
export const OrgRemoveMemberRequestSchema = z.object({
|
|
organisationId: z.number().int().positive("organisationId must be a positive integer"),
|
|
userId: z.number().int().positive("userId must be a positive integer"),
|
|
});
|
|
|
|
export type OrgRemoveMemberRequest = z.infer<typeof OrgRemoveMemberRequestSchema>;
|
|
|
|
export const OrgUpdateMemberRoleRequestSchema = z.object({
|
|
organisationId: z.number().int().positive("organisationId must be a positive integer"),
|
|
userId: z.number().int().positive("userId must be a positive integer"),
|
|
role: z.enum(["admin", "member"]),
|
|
});
|
|
|
|
export type OrgUpdateMemberRoleRequest = z.infer<typeof OrgUpdateMemberRoleRequestSchema>;
|
|
|
|
// project schemas
|
|
|
|
export const ProjectCreateRequestSchema = z.object({
|
|
name: z.string().min(1, "Name is required").max(PROJECT_NAME_MAX_LENGTH),
|
|
key: z
|
|
.string()
|
|
.min(1, "Key is required")
|
|
.max(4, "Key must be 4 characters or less")
|
|
.regex(/^[A-Za-z]{1,4}$/, "Key must be only letters A-Z"),
|
|
organisationId: z.number().int().positive("organisationId must be a positive integer"),
|
|
});
|
|
|
|
export type ProjectCreateRequest = z.infer<typeof ProjectCreateRequestSchema>;
|
|
|
|
export const ProjectUpdateRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
name: z.string().min(1, "Name must be at least 1 character").max(PROJECT_NAME_MAX_LENGTH).optional(),
|
|
key: z
|
|
.string()
|
|
.min(1, "Key is required")
|
|
.max(4, "Key must be 4 characters or less")
|
|
.regex(/^[A-Za-z]{1,4}$/, "Key must be only letters A-Z")
|
|
.optional(),
|
|
creatorId: z.number().int().positive().optional(),
|
|
organisationId: z.number().int().positive().optional(),
|
|
});
|
|
|
|
export type ProjectUpdateRequest = z.infer<typeof ProjectUpdateRequestSchema>;
|
|
|
|
export const ProjectDeleteRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type ProjectDeleteRequest = z.infer<typeof ProjectDeleteRequestSchema>;
|
|
|
|
export const ProjectByOrgQuerySchema = z.object({
|
|
organisationId: z.coerce.number().int().positive("organisationId must be a positive integer"),
|
|
});
|
|
|
|
export type ProjectByOrgQuery = z.infer<typeof ProjectByOrgQuerySchema>;
|
|
|
|
export const ProjectByIdQuerySchema = z.object({
|
|
id: z.coerce.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type ProjectByIdQuery = z.infer<typeof ProjectByIdQuerySchema>;
|
|
|
|
export const ProjectByCreatorQuerySchema = z.object({
|
|
creatorId: z.coerce.number().int().positive("creatorId must be a positive integer"),
|
|
});
|
|
|
|
export type ProjectByCreatorQuery = z.infer<typeof ProjectByCreatorQuerySchema>;
|
|
|
|
// sprint schemas
|
|
|
|
export const SprintCreateRequestSchema = z
|
|
.object({
|
|
projectId: z.number().int().positive("projectId must be a positive integer"),
|
|
name: z.string().min(1, "Name is required").max(64, "Name must be at most 64 characters"),
|
|
color: z
|
|
.string()
|
|
.regex(/^#[0-9a-fA-F]{6}$/, "Color must be a valid hex color")
|
|
.optional(),
|
|
startDate: z.string().datetime("Start date must be a valid date"),
|
|
endDate: z.string().datetime("End date must be a valid date"),
|
|
})
|
|
.refine((data) => new Date(data.startDate) <= new Date(data.endDate), {
|
|
message: "End date must be after start date",
|
|
path: ["endDate"],
|
|
});
|
|
|
|
export type SprintCreateRequest = z.infer<typeof SprintCreateRequestSchema>;
|
|
|
|
export const SprintUpdateRequestSchema = z
|
|
.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
name: z.string().min(1, "Name is required").max(64, "Name must be at most 64 characters").optional(),
|
|
color: z
|
|
.string()
|
|
.regex(/^#[0-9a-fA-F]{6}$/, "Color must be a valid hex color")
|
|
.optional(),
|
|
startDate: z.string().datetime("Start date must be a valid date").optional(),
|
|
endDate: z.string().datetime("End date must be a valid date").optional(),
|
|
})
|
|
.refine(
|
|
(data) => {
|
|
if (data.startDate && data.endDate) {
|
|
return new Date(data.startDate) <= new Date(data.endDate);
|
|
}
|
|
return true;
|
|
},
|
|
{
|
|
message: "End date must be after start date",
|
|
path: ["endDate"],
|
|
},
|
|
);
|
|
|
|
export type SprintUpdateRequest = z.infer<typeof SprintUpdateRequestSchema>;
|
|
|
|
export const SprintDeleteRequestSchema = z.object({
|
|
id: z.number().int().positive("id must be a positive integer"),
|
|
});
|
|
|
|
export type SprintDeleteRequest = z.infer<typeof SprintDeleteRequestSchema>;
|
|
|
|
export const SprintsByProjectQuerySchema = z.object({
|
|
projectId: z.coerce.number().int().positive("projectId must be a positive integer"),
|
|
});
|
|
|
|
export type SprintsByProjectQuery = z.infer<typeof SprintsByProjectQuerySchema>;
|
|
|
|
// timer schemas
|
|
|
|
export const TimerToggleRequestSchema = z.object({
|
|
issueId: z.number().int().positive("issueId must be a positive integer"),
|
|
});
|
|
|
|
export type TimerToggleRequest = z.infer<typeof TimerToggleRequestSchema>;
|
|
|
|
export const TimerEndRequestSchema = z.object({
|
|
issueId: z.number().int().positive("issueId must be a positive integer"),
|
|
});
|
|
|
|
export type TimerEndRequest = z.infer<typeof TimerEndRequestSchema>;
|
|
|
|
export const TimerGetQuerySchema = z.object({
|
|
issueId: z.coerce.number().int().positive("issueId must be a positive integer"),
|
|
});
|
|
|
|
export type TimerGetQuery = z.infer<typeof TimerGetQuerySchema>;
|
|
|
|
// user schemas
|
|
|
|
export const UserUpdateRequestSchema = z.object({
|
|
name: z.string().min(1, "Name must be at least 1 character").max(USER_NAME_MAX_LENGTH).optional(),
|
|
password: z
|
|
.string()
|
|
.min(8, "Password must be at least 8 characters")
|
|
.regex(/[A-Z]/, "Password must contain an uppercase letter")
|
|
.regex(/[a-z]/, "Password must contain a lowercase letter")
|
|
.regex(/[0-9]/, "Password must contain a number")
|
|
.optional(),
|
|
avatarURL: z.string().url().nullable().optional(),
|
|
iconPreference: z.enum(["lucide", "pixel", "phosphor"]).optional(),
|
|
});
|
|
|
|
export type UserUpdateRequest = z.infer<typeof UserUpdateRequestSchema>;
|
|
|
|
export const UserByUsernameQuerySchema = z.object({
|
|
username: z.string().min(1, "Username is required"),
|
|
});
|
|
|
|
export type UserByUsernameQuery = z.infer<typeof UserByUsernameQuerySchema>;
|
|
|
|
// response schemas
|
|
|
|
export const UserResponseSchema = z.object({
|
|
id: z.number(),
|
|
name: z.string(),
|
|
username: z.string(),
|
|
avatarURL: z.string().nullable(),
|
|
iconPreference: z.enum(["lucide", "pixel", "phosphor"]),
|
|
plan: z.string().nullable().optional(),
|
|
createdAt: z.string().nullable().optional(),
|
|
updatedAt: z.string().nullable().optional(),
|
|
});
|
|
|
|
export type UserResponse = z.infer<typeof UserResponseSchema>;
|
|
|
|
export const IssueRecordSchema = z.object({
|
|
id: z.number(),
|
|
projectId: z.number(),
|
|
number: z.number(),
|
|
type: z.string(),
|
|
status: z.string(),
|
|
title: z.string(),
|
|
description: z.string(),
|
|
creatorId: z.number(),
|
|
sprintId: z.number().nullable(),
|
|
});
|
|
|
|
export const IssueResponseSchema = z.object({
|
|
Issue: IssueRecordSchema,
|
|
Creator: UserResponseSchema,
|
|
Assignees: z.array(UserResponseSchema),
|
|
});
|
|
|
|
export type IssueResponse = z.infer<typeof IssueResponseSchema>;
|
|
|
|
export const IssueCommentRecordSchema = z.object({
|
|
id: z.number(),
|
|
issueId: z.number(),
|
|
userId: z.number(),
|
|
body: z.string(),
|
|
createdAt: z.string().nullable().optional(),
|
|
updatedAt: z.string().nullable().optional(),
|
|
});
|
|
|
|
export const IssueCommentResponseSchema = z.object({
|
|
Comment: IssueCommentRecordSchema,
|
|
User: UserResponseSchema,
|
|
});
|
|
|
|
export type IssueCommentResponse = z.infer<typeof IssueCommentResponseSchema>;
|
|
|
|
export const OrganisationRecordSchema = z.object({
|
|
id: z.number(),
|
|
name: z.string(),
|
|
description: z.string().nullable(),
|
|
slug: z.string(),
|
|
iconURL: z.string().nullable().optional(),
|
|
statuses: z.record(z.string()),
|
|
features: z.record(z.boolean()),
|
|
issueTypes: z.record(z.object({ icon: z.string(), color: z.string() })),
|
|
createdAt: z.string().nullable().optional(),
|
|
updatedAt: z.string().nullable().optional(),
|
|
});
|
|
|
|
export type OrganisationRecordType = z.infer<typeof OrganisationRecordSchema>;
|
|
|
|
export const OrganisationMemberRecordSchema = z.object({
|
|
id: z.number(),
|
|
organisationId: z.number(),
|
|
userId: z.number(),
|
|
role: z.string(),
|
|
createdAt: z.string().nullable().optional(),
|
|
});
|
|
|
|
export type OrganisationMemberRecordType = z.infer<typeof OrganisationMemberRecordSchema>;
|
|
|
|
export const OrganisationResponseSchema = z.object({
|
|
Organisation: OrganisationRecordSchema,
|
|
OrganisationMember: OrganisationMemberRecordSchema,
|
|
});
|
|
|
|
export type OrganisationResponse = z.infer<typeof OrganisationResponseSchema>;
|
|
|
|
export const OrganisationMemberResponseSchema = z.object({
|
|
OrganisationMember: OrganisationMemberRecordSchema,
|
|
Organisation: OrganisationRecordSchema,
|
|
User: UserResponseSchema,
|
|
});
|
|
|
|
export type OrganisationMemberResponse = z.infer<typeof OrganisationMemberResponseSchema>;
|
|
|
|
export const ProjectRecordSchema = z.object({
|
|
id: z.number(),
|
|
key: z.string(),
|
|
name: z.string(),
|
|
organisationId: z.number(),
|
|
creatorId: z.number(),
|
|
});
|
|
|
|
export const ProjectResponseSchema = z.object({
|
|
Project: ProjectRecordSchema,
|
|
Organisation: OrganisationRecordSchema,
|
|
User: UserResponseSchema,
|
|
});
|
|
|
|
export type ProjectResponse = z.infer<typeof ProjectResponseSchema>;
|
|
|
|
export const ProjectWithCreatorResponseSchema = z.object({
|
|
Project: ProjectRecordSchema,
|
|
User: UserResponseSchema,
|
|
});
|
|
|
|
export type ProjectWithCreatorResponse = z.infer<typeof ProjectWithCreatorResponseSchema>;
|
|
|
|
export const SprintRecordSchema = z.object({
|
|
id: z.number(),
|
|
projectId: z.number(),
|
|
name: z.string(),
|
|
color: z.string(),
|
|
startDate: z.string(),
|
|
endDate: z.string(),
|
|
createdAt: z.string().nullable().optional(),
|
|
});
|
|
|
|
export type SprintResponse = z.infer<typeof SprintRecordSchema>;
|
|
|
|
export const TimerStateSchema = z
|
|
.object({
|
|
id: z.number(),
|
|
workTimeMs: z.number(),
|
|
breakTimeMs: z.number(),
|
|
isRunning: z.boolean(),
|
|
timestamps: z.array(z.string()),
|
|
endedAt: z.string().nullable(),
|
|
})
|
|
.nullable();
|
|
|
|
export type TimerStateType = z.infer<typeof TimerStateSchema>;
|
|
|
|
export const TimerListItemSchema = z.object({
|
|
id: z.number(),
|
|
issueId: z.number(),
|
|
issueNumber: z.number(),
|
|
projectKey: z.string(),
|
|
workTimeMs: z.number(),
|
|
breakTimeMs: z.number(),
|
|
isRunning: z.boolean(),
|
|
timestamps: z.array(z.string()),
|
|
endedAt: z.string().nullable(),
|
|
});
|
|
|
|
export type TimerListItem = z.infer<typeof TimerListItemSchema>;
|
|
|
|
export const TimerListResponseSchema = z.array(TimerListItemSchema);
|
|
|
|
export type TimerListResponse = z.infer<typeof TimerListResponseSchema>;
|
|
|
|
export const StatusCountResponseSchema = z.array(
|
|
z.object({
|
|
status: z.string(),
|
|
count: z.number(),
|
|
}),
|
|
);
|
|
|
|
export type StatusCountResponse = z.infer<typeof StatusCountResponseSchema>;
|
|
|
|
export const ReplaceStatusResponseSchema = z.object({
|
|
rowCount: z.number(),
|
|
});
|
|
|
|
export type ReplaceStatusResponse = z.infer<typeof ReplaceStatusResponseSchema>;
|
|
|
|
export const TypeCountResponseSchema = z.object({
|
|
count: z.number(),
|
|
});
|
|
|
|
export type TypeCountResponse = z.infer<typeof TypeCountResponseSchema>;
|
|
|
|
export const ReplaceTypeResponseSchema = z.object({
|
|
rowCount: z.number(),
|
|
});
|
|
|
|
export type ReplaceTypeResponse = z.infer<typeof ReplaceTypeResponseSchema>;
|
|
|
|
// general
|
|
|
|
export const SuccessResponseSchema = z.object({
|
|
success: z.boolean(),
|
|
});
|
|
|
|
export type SuccessResponse = z.infer<typeof SuccessResponseSchema>;
|
|
|
|
// subscription schemas
|
|
|
|
export const CreateCheckoutSessionRequestSchema = z.object({
|
|
billingPeriod: z.enum(["monthly", "annual"]),
|
|
});
|
|
|
|
export type CreateCheckoutSessionRequest = z.infer<typeof CreateCheckoutSessionRequestSchema>;
|
|
|
|
export const CreateCheckoutSessionResponseSchema = z.object({
|
|
url: z.string(),
|
|
});
|
|
|
|
export type CreateCheckoutSessionResponse = z.infer<typeof CreateCheckoutSessionResponseSchema>;
|
|
|
|
export const CreatePortalSessionResponseSchema = z.object({
|
|
url: z.string(),
|
|
});
|
|
|
|
export type CreatePortalSessionResponse = z.infer<typeof CreatePortalSessionResponseSchema>;
|
|
|
|
export const SubscriptionRecordSchema = z.object({
|
|
id: z.number(),
|
|
userId: z.number(),
|
|
stripeCustomerId: z.string().nullable(),
|
|
stripeSubscriptionId: z.string().nullable(),
|
|
stripeSubscriptionItemId: z.string().nullable(),
|
|
stripePriceId: z.string().nullable(),
|
|
status: z.string(),
|
|
currentPeriodStart: z.string().nullable().optional(),
|
|
currentPeriodEnd: z.string().nullable().optional(),
|
|
cancelAtPeriodEnd: z.boolean(),
|
|
trialEnd: z.string().nullable().optional(),
|
|
quantity: z.number(),
|
|
createdAt: z.string().nullable().optional(),
|
|
updatedAt: z.string().nullable().optional(),
|
|
});
|
|
|
|
export type SubscriptionRecord = z.infer<typeof SubscriptionRecordSchema>;
|
|
|
|
export const GetSubscriptionResponseSchema = z.object({
|
|
subscription: SubscriptionRecordSchema.nullable(),
|
|
});
|
|
|
|
export type GetSubscriptionResponse = z.infer<typeof GetSubscriptionResponseSchema>;
|
|
|
|
export const CancelSubscriptionResponseSchema = z.object({
|
|
subscription: SubscriptionRecordSchema,
|
|
});
|
|
|
|
export type CancelSubscriptionResponse = z.infer<typeof CancelSubscriptionResponseSchema>;
|