mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-10-13 00:03:18 +01:00
Add goals feature for personal goals with filters
This commit is contained in:
@@ -412,6 +412,94 @@ const ProjectMemberUpdateRequest = z
|
||||
.object({ billable_rate: z.union([z.number(), z.null()]) })
|
||||
.partial()
|
||||
.passthrough();
|
||||
const GoalComparison = z.enum(['at_least', 'less_than']);
|
||||
const GoalPeriod = z.enum(['day', 'week', 'month']);
|
||||
const GoalType = z.enum(['personal', 'organization']);
|
||||
const Weekday = z.enum([
|
||||
'monday',
|
||||
'tuesday',
|
||||
'wednesday',
|
||||
'thursday',
|
||||
'friday',
|
||||
'saturday',
|
||||
'sunday',
|
||||
]);
|
||||
const GoalStatus = z.enum(['in_progress', 'achieved', 'on_track', 'exceeded']);
|
||||
const GoalResource = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
type: GoalType,
|
||||
comparison: GoalComparison,
|
||||
target_seconds: z.number().int(),
|
||||
period: GoalPeriod,
|
||||
member_id: z.union([z.string(), z.null()]),
|
||||
member_name: z.union([z.string(), z.null()]),
|
||||
timezone: z.string(),
|
||||
week_start: Weekday,
|
||||
is_archived: z.boolean(),
|
||||
filters: z
|
||||
.object({
|
||||
member_ids: z.union([z.array(z.string()), z.null()]),
|
||||
project_ids: z.union([z.array(z.string()), z.null()]),
|
||||
task_ids: z.union([z.array(z.string()), z.null()]),
|
||||
tag_ids: z.union([z.array(z.string()), z.null()]),
|
||||
tag_match_type: z.union([z.enum(['contains', 'not_contains']), z.null()]),
|
||||
client_ids: z.union([z.array(z.string()), z.null()]),
|
||||
billable: z.union([z.boolean(), z.null()]),
|
||||
time_entry_type: z.union([z.enum(['work', 'break']), z.null()]),
|
||||
})
|
||||
.passthrough(),
|
||||
progress: z
|
||||
.object({
|
||||
period_start: z.string(),
|
||||
period_end: z.string(),
|
||||
tracked_seconds: z.number().int(),
|
||||
status: GoalStatus,
|
||||
})
|
||||
.passthrough(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
const GoalFiltersRequest = z
|
||||
.object({
|
||||
member_ids: z.union([z.array(z.string().uuid()), z.null()]).optional(),
|
||||
project_ids: z.union([z.array(z.string()), z.null()]).optional(),
|
||||
task_ids: z.union([z.array(z.string()), z.null()]).optional(),
|
||||
tag_ids: z.union([z.array(z.string()), z.null()]).optional(),
|
||||
tag_match_type: z.union([z.enum(['contains', 'not_contains']), z.null()]).optional(),
|
||||
client_ids: z.union([z.array(z.string()), z.null()]).optional(),
|
||||
billable: z.union([z.boolean(), z.null()]).optional(),
|
||||
time_entry_type: z.union([z.enum(['work', 'break']), z.null()]).optional(),
|
||||
})
|
||||
.passthrough();
|
||||
const GoalStoreRequest = z
|
||||
.object({
|
||||
name: z.string().max(255),
|
||||
type: GoalType,
|
||||
comparison: GoalComparison,
|
||||
target_seconds: z.number().int().gte(1).lte(2147483647),
|
||||
period: GoalPeriod,
|
||||
member_id: z.union([z.string().uuid(), z.null()]).optional(),
|
||||
timezone: z.union([z.string(), z.null()]).optional(),
|
||||
week_start: z.union([Weekday, z.null()]).optional(),
|
||||
filters: GoalFiltersRequest.optional(),
|
||||
})
|
||||
.passthrough();
|
||||
const GoalUpdateRequest = z
|
||||
.object({
|
||||
name: z.string().max(255),
|
||||
comparison: GoalComparison,
|
||||
target_seconds: z.number().int().gte(1).lte(2147483647),
|
||||
period: GoalPeriod,
|
||||
timezone: z.string(),
|
||||
week_start: Weekday,
|
||||
is_archived: z.boolean(),
|
||||
filters: GoalFiltersRequest,
|
||||
})
|
||||
.partial()
|
||||
.passthrough();
|
||||
const ReportResource = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
@@ -439,15 +527,6 @@ const TimeEntryAggregationType = z.enum([
|
||||
'type',
|
||||
]);
|
||||
const TimeEntryAggregationTypeInterval = z.enum(['day', 'week', 'month', 'year']);
|
||||
const Weekday = z.enum([
|
||||
'monday',
|
||||
'tuesday',
|
||||
'wednesday',
|
||||
'thursday',
|
||||
'friday',
|
||||
'saturday',
|
||||
'sunday',
|
||||
]);
|
||||
const TimeEntryRoundingType = z.enum(['up', 'down', 'nearest']);
|
||||
const ReportStoreRequest = z
|
||||
.object({
|
||||
@@ -782,10 +861,18 @@ export const schemas = {
|
||||
ProjectMemberResource,
|
||||
ProjectMemberStoreRequest,
|
||||
ProjectMemberUpdateRequest,
|
||||
GoalComparison,
|
||||
GoalPeriod,
|
||||
GoalType,
|
||||
Weekday,
|
||||
GoalStatus,
|
||||
GoalResource,
|
||||
GoalFiltersRequest,
|
||||
GoalStoreRequest,
|
||||
GoalUpdateRequest,
|
||||
ReportResource,
|
||||
TimeEntryAggregationType,
|
||||
TimeEntryAggregationTypeInterval,
|
||||
Weekday,
|
||||
TimeEntryRoundingType,
|
||||
ReportStoreRequest,
|
||||
DetailedReportResource,
|
||||
@@ -833,6 +920,13 @@ const endpoints = makeApi([
|
||||
z.object({ code: z.string(), name: z.string(), symbol: z.string() }).passthrough()
|
||||
),
|
||||
},
|
||||
{
|
||||
method: 'get',
|
||||
path: '/v1/time-zones',
|
||||
alias: 'getTimezones',
|
||||
requestFormat: 'json',
|
||||
response: z.array(z.object({ key: z.string() }).passthrough()),
|
||||
},
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v1/organizations',
|
||||
@@ -3182,6 +3276,254 @@ const endpoints = makeApi([
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: 'get',
|
||||
path: '/v1/organizations/:organization/goals',
|
||||
alias: 'getGoals',
|
||||
requestFormat: 'json',
|
||||
parameters: [
|
||||
{
|
||||
name: 'organization',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'Query',
|
||||
schema: z.union([GoalType, z.null()]).optional(),
|
||||
},
|
||||
{
|
||||
name: 'archived',
|
||||
type: 'Query',
|
||||
schema: z.union([z.enum(['true', 'false', 'all']), z.null()]).optional(),
|
||||
},
|
||||
{
|
||||
name: 'page',
|
||||
type: 'Query',
|
||||
schema: z.number().int().gte(1).lte(2147483647).optional(),
|
||||
},
|
||||
],
|
||||
response: z
|
||||
.object({
|
||||
data: z.array(GoalResource),
|
||||
links: z
|
||||
.object({
|
||||
first: z.union([z.string(), z.null()]),
|
||||
last: z.union([z.string(), z.null()]),
|
||||
prev: z.union([z.string(), z.null()]),
|
||||
next: z.union([z.string(), z.null()]),
|
||||
})
|
||||
.passthrough(),
|
||||
meta: z
|
||||
.object({
|
||||
current_page: z.number().int(),
|
||||
from: z.union([z.number(), z.null()]),
|
||||
last_page: z.number().int(),
|
||||
links: z.array(
|
||||
z
|
||||
.object({
|
||||
url: z.union([z.string(), z.null()]),
|
||||
label: z.string(),
|
||||
active: z.boolean(),
|
||||
})
|
||||
.passthrough()
|
||||
),
|
||||
path: z.union([z.string(), z.null()]),
|
||||
per_page: z.number().int(),
|
||||
to: z.union([z.number(), z.null()]),
|
||||
total: z.number().int(),
|
||||
})
|
||||
.passthrough(),
|
||||
})
|
||||
.passthrough(),
|
||||
errors: [
|
||||
{
|
||||
status: 401,
|
||||
description: `Unauthenticated`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
description: `Authorization error`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
description: `Not found`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 422,
|
||||
description: `Validation error`,
|
||||
schema: z
|
||||
.object({ message: z.string(), errors: z.record(z.array(z.string())) })
|
||||
.passthrough(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v1/organizations/:organization/goals',
|
||||
alias: 'createGoal',
|
||||
requestFormat: 'json',
|
||||
parameters: [
|
||||
{
|
||||
name: 'body',
|
||||
type: 'Body',
|
||||
schema: GoalStoreRequest,
|
||||
},
|
||||
{
|
||||
name: 'organization',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
],
|
||||
response: z.object({ data: GoalResource }).passthrough(),
|
||||
errors: [
|
||||
{
|
||||
status: 401,
|
||||
description: `Unauthenticated`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
description: `Authorization error`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
description: `Not found`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 422,
|
||||
description: `Validation error`,
|
||||
schema: z
|
||||
.object({ message: z.string(), errors: z.record(z.array(z.string())) })
|
||||
.passthrough(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: 'get',
|
||||
path: '/v1/organizations/:organization/goals/:goal',
|
||||
alias: 'getGoal',
|
||||
requestFormat: 'json',
|
||||
parameters: [
|
||||
{
|
||||
name: 'organization',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
{
|
||||
name: 'goal',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
],
|
||||
response: z.object({ data: GoalResource }).passthrough(),
|
||||
errors: [
|
||||
{
|
||||
status: 401,
|
||||
description: `Unauthenticated`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
description: `Authorization error`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
description: `Not found`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: 'put',
|
||||
path: '/v1/organizations/:organization/goals/:goal',
|
||||
alias: 'updateGoal',
|
||||
requestFormat: 'json',
|
||||
parameters: [
|
||||
{
|
||||
name: 'body',
|
||||
type: 'Body',
|
||||
schema: GoalUpdateRequest,
|
||||
},
|
||||
{
|
||||
name: 'organization',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
{
|
||||
name: 'goal',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
],
|
||||
response: z.object({ data: GoalResource }).passthrough(),
|
||||
errors: [
|
||||
{
|
||||
status: 401,
|
||||
description: `Unauthenticated`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
description: `Authorization error`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
description: `Not found`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 422,
|
||||
description: `Validation error`,
|
||||
schema: z
|
||||
.object({ message: z.string(), errors: z.record(z.array(z.string())) })
|
||||
.passthrough(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: 'delete',
|
||||
path: '/v1/organizations/:organization/goals/:goal',
|
||||
alias: 'deleteGoal',
|
||||
requestFormat: 'json',
|
||||
parameters: [
|
||||
{
|
||||
name: 'organization',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
{
|
||||
name: 'goal',
|
||||
type: 'Path',
|
||||
schema: z.string(),
|
||||
},
|
||||
],
|
||||
response: z.void(),
|
||||
errors: [
|
||||
{
|
||||
status: 401,
|
||||
description: `Unauthenticated`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
description: `Authorization error`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
description: `Not found`,
|
||||
schema: z.object({ message: z.string() }).passthrough(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: 'get',
|
||||
path: '/v1/organizations/:organization/reports',
|
||||
|
||||
Reference in New Issue
Block a user