Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: project cycle number #96

Merged
merged 3 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/server/migrations/0018_project_cycles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type Kysely } from "kysely";

export async function up(db: Kysely<unknown>): Promise<void> {
await db.schema
.alterTable("project")
.addColumn("num_cycles", "integer", (col) => col.notNull().defaultTo(1))
.execute();
}

export async function down(db: Kysely<unknown>): Promise<void> {
await db.schema.alterTable("project").dropColumn("num_cycles").execute();
}
1 change: 1 addition & 0 deletions apps/server/src/db/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export async function populateExample(
name: "Pi-5 Test Profile",
partVariationId: pi58GB.id,
workspaceId,
numCycles: 1,
})
).safeUnwrap();

Expand Down
27 changes: 27 additions & 0 deletions apps/web/src/components/project/new-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default function NewProjectButton({ workspace, partVariations }: Props) {
resolver: typeboxResolver(CreateProjectSchema),
defaultValues: {
workspaceId: workspace.id,
numCycles: 1,
},
});

Expand Down Expand Up @@ -220,6 +221,32 @@ export default function NewProjectButton({ workspace, partVariations }: Props) {
</FormItem>
)}
/>
<FormField
control={form.control}
name="numCycles"
render={({ field }) => (
<FormItem>
<FormLabel>Number of Cycles</FormLabel>
<FormControl>
<Input
type="number"
value={field.value}
min={-1}
onChange={(e) =>
form.setValue("numCycles", parseInt(e.target.value))
}
className="w-32"
data-1p-ignore
/>
</FormControl>
<FormDescription>
How many cycles will you run each test session for? (-1
for infinite)
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="py-1" />
<DialogFooter className="">
<DialogClose asChild>
Expand Down
37 changes: 33 additions & 4 deletions apps/web/src/components/settings/project-general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const ProjectGeneral = ({ workspace, projectPerm, project }: Props) => {
defaultValues: {
name: project.name,
repoUrl: project.repoUrl ?? undefined,
numCycles: project.numCycles,
},
});

Expand Down Expand Up @@ -170,6 +171,35 @@ const ProjectGeneral = ({ workspace, projectPerm, project }: Props) => {
</FormItem>
)}
/>
<FormField
control={updateProjectForm.control}
name="numCycles"
render={({ field }) => (
<FormItem>
<FormLabel className="text-xl">Number of Cycles</FormLabel>
<FormDescription>
The default number of cycles to run for each test session
(-1 for infinite).
</FormDescription>
<FormControl>
<Input
disabled={!projectPerm.canAdmin()}
type="number"
value={field.value}
className="w-32"
onChange={(e) =>
updateProjectForm.setValue(
"numCycles",
parseInt(e.target.value),
)
}
data-1p-ignore
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{updateProjectForm.formState.isSubmitting ? (
<Button disabled={true}>
<Icons.spinner className="h-6 w-6" />
Expand All @@ -183,10 +213,9 @@ const ProjectGeneral = ({ workspace, projectPerm, project }: Props) => {
{projectPerm.canAdmin() && (
<Card className="border-destructive">
<CardHeader>
<CardTitle className="text-xl">Delete Production Line</CardTitle>
<CardTitle className="text-xl">Delete Test Profile</CardTitle>
<CardDescription>
This will permanently delete your production line and all its
data.
This will permanently delete your test profile and all its data.
</CardDescription>
</CardHeader>
<CardFooter className="space-x-4 border-t px-6 py-4">
Expand All @@ -199,7 +228,7 @@ const ProjectGeneral = ({ workspace, projectPerm, project }: Props) => {
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete
your production line and remove your data from our servers.
your test profile and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/lib/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const removePrefix = (value: string, prefix: string) =>
value.startsWith(prefix) ? value.slice(prefix.length) : value;

export const pluralize = (s: string, count: number) =>
count === 1 ? s : `${s}s`;
3 changes: 0 additions & 3 deletions apps/web/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,3 @@ export const makeQueryParams = <T extends Record<PropertyKey, unknown>>(
typedObjectFromEntries(
typedObjectEntries(params).filter(([, v]) => v !== undefined),
);

export const removePrefix = (value: string, prefix: string) =>
value.startsWith(prefix) ? value.slice(prefix.length) : value;
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
getPartVariationQueryOpts,
getPartVariationsQueryOpts,
} from "@/lib/queries/part-variation";
import { removePrefix } from "@/lib/utils";
import { removePrefix } from "@/lib/string";
import { Route as WorkspaceIndexRoute } from "@/routes/_protected/workspace/$namespace";
import { PartVariation, PartVariationTreeNode } from "@cloud/shared";
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
import { getPartVariationQueryOpts } from "@/lib/queries/part-variation";
import { getStationsQueryOpts } from "@/lib/queries/station";
import { makeTimeSeriesData } from "@/lib/stats";
import { pluralize } from "@/lib/string";
import { pastTimeFromBin } from "@/lib/time";
import { cn } from "@/lib/utils";
import { Test, TimePeriod, Unit } from "@cloud/shared";
Expand All @@ -55,6 +56,7 @@ import {
Cpu,
GitPullRequest,
Hash,
IterationCcw,
LucideIcon,
PercentSquare,
Settings,
Expand Down Expand Up @@ -354,6 +356,16 @@ function Page() {
<span>Git upstream not set</span>
)}
</div>
<div className="py-1" />
<div className="items-center flex text-sm font-medium">
<IterationCcw size={20} />
<div className="px-1" />
<div>
{project.numCycles === -1
? "Infinite"
: `${project.numCycles} ${pluralize("cycle", project.numCycles)}`}
</div>
</div>
</PageHeaderDescription>
</PageHeader>

Expand Down
3 changes: 3 additions & 0 deletions packages/shared/src/schemas/public/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export default interface ProjectTable {

/** Database type: pg_catalog.text */
repoUrl: ColumnType<string | null, string | null, string | null>;

/** Database type: pg_catalog.int4 */
numCycles: ColumnType<number, number | undefined, number>;
}

export type Project = Selectable<ProjectTable>;
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/src/types/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import { User } from "../schemas/public/User";
export type { Project } from "../schemas/public/Project";
export type { ProjectUser } from "../schemas/public/ProjectUser";

const cycleCount = t.Union([t.Literal(-1), t.Number({ minimum: 1 })], {
error: "Number of cycles must be -1 or greater than 0",
});

export const CreateProjectSchema = t.Object({
name: t.String({ minLength: 1 }),
partVariationId: t.String({ minLength: 1 }),
numCycles: cycleCount,
workspaceId: t.String(),
repoUrl: t.Optional(t.String()),
});
Expand All @@ -16,6 +21,7 @@ export type CreateProjectSchema = Static<typeof CreateProjectSchema>;

export const UpdateProjectSchema = t.Object({
name: t.Optional(t.String({ minLength: 1 })),
numCycles: t.Optional(cycleCount),
repoUrl: t.Optional(t.String()),
});

Expand Down
Loading