Skip to content

Commit

Permalink
fix: Select release channel filter on deployment page
Browse files Browse the repository at this point in the history
  • Loading branch information
adityachoudhari26 committed Nov 12, 2024
1 parent 929bee2 commit 8cc0a72
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {

import { api } from "~/trpc/react";
import { ReleaseConditionRender } from "../release-condition/ReleaseConditionRender";
import { useReleaseFilter } from "../release-condition/useReleaseFilter";
import { ReleaseBadgeList } from "../ReleaseBadgeList";

type OverviewProps = {
Expand Down Expand Up @@ -86,6 +87,7 @@ export const Overview: React.FC<OverviewProps> = ({ releaseChannel }) => {
systemSlug?: string;
deploymentSlug?: string;
}>();
const { filter: paramFilter, setFilter } = useReleaseFilter();

const defaultValues = {
...releaseChannel,
Expand All @@ -106,6 +108,9 @@ export const Overview: React.FC<OverviewProps> = ({ releaseChannel }) => {
.then(() =>
utils.deployment.releaseChannel.byId.invalidate(releaseChannel.id),
)
.then(() => {
if (paramFilter != null) setFilter(releaseFilter);
})
.then(() => router.refresh());
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from "@ctrlplane/ui/dropdown-menu";

import { api } from "~/trpc/react";
import { useReleaseFilter } from "../release-condition/useReleaseFilter";
import { useReleaseChannelDrawer } from "./useReleaseChannelDrawer";

type DeleteReleaseChannelDialogProps = {
Expand All @@ -37,13 +38,15 @@ const DeleteReleaseChannelDialog: React.FC<DeleteReleaseChannelDialogProps> = ({
}) => {
const [open, setOpen] = useState(false);
const { removeReleaseChannelId } = useReleaseChannelDrawer();
const { removeReleaseChannel } = useReleaseFilter();
const router = useRouter();
const deleteReleaseChannel =
api.deployment.releaseChannel.delete.useMutation();
const onDelete = () =>
deleteReleaseChannel
.mutateAsync(releaseChannelId)
.then(() => removeReleaseChannelId())
.then(() => removeReleaseChannel())
.then(() => router.refresh())
.then(() => setOpen(false));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import type * as SCHEMA from "@ctrlplane/db/schema";
import type { ReleaseCondition } from "@ctrlplane/validators/releases";
import React, { useState } from "react";

Expand All @@ -13,6 +14,16 @@ import {
DialogTitle,
DialogTrigger,
} from "@ctrlplane/ui/dialog";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@ctrlplane/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ctrlplane/ui/tabs";
import {
defaultCondition,
isValidReleaseCondition,
Expand All @@ -22,26 +33,37 @@ import {
import { api } from "~/trpc/react";
import { ReleaseBadgeList } from "../ReleaseBadgeList";
import { ReleaseConditionRender } from "./ReleaseConditionRender";
import { useReleaseFilter } from "./useReleaseFilter";

type ReleaseConditionDialogProps = {
condition?: ReleaseCondition;
deploymentId?: string;
onChange: (condition: ReleaseCondition | undefined) => void;
releaseChannels?: SCHEMA.ReleaseChannel[];
children: React.ReactNode;
};

export const ReleaseConditionDialog: React.FC<ReleaseConditionDialogProps> = ({
condition,
deploymentId,
onChange,
releaseChannels = [],
children,
}) => {
const [open, setOpen] = useState(false);
const [error, setError] = useState<string | null>(null);
const [localCondition, setLocalCondition] = useState(
condition ?? defaultCondition,
);
const isLocalConditionValid = isValidReleaseCondition(localCondition);
const { releaseChannelId, setReleaseChannel, removeReleaseChannel } =
useReleaseFilter();

const [localReleaseChannelId, setLocalReleaseChannelId] = useState<
string | undefined
>(releaseChannelId);

const [localCondition, setLocalCondition] = useState<
ReleaseCondition | undefined
>(condition ?? defaultCondition);
const isLocalConditionValid =
localCondition == null || isValidReleaseCondition(localCondition);
const releasesQ = api.release.list.useQuery(
{ deploymentId: deploymentId ?? "", filter: localCondition, limit: 5 },
{ enabled: deploymentId != null && isLocalConditionValid },
Expand All @@ -55,45 +77,125 @@ export const ReleaseConditionDialog: React.FC<ReleaseConditionDialogProps> = ({
className="min-w-[1000px]"
onClick={(e) => e.stopPropagation()}
>
<DialogHeader>
<DialogTitle>Edit Release Condition</DialogTitle>
<DialogDescription>
Edit the release filter, up to a depth of {MAX_DEPTH_ALLOWED + 1}.
</DialogDescription>
</DialogHeader>
<ReleaseConditionRender
condition={localCondition}
onChange={setLocalCondition}
/>
{releases != null && <ReleaseBadgeList releases={releases} />}
{error && <span className="text-sm text-red-600">{error}</span>}
<DialogFooter>
<Button
variant="outline"
onClick={() => {
<Tabs
defaultValue={
releaseChannels.length > 0 ? "release-channels" : "new-filter"
}
className="space-y-4"
onValueChange={(value) => {
if (value === "new-filter") {
setLocalCondition(defaultCondition);
setError(null);
}}
>
Clear
</Button>
<div className="flex-grow" />
<Button
onClick={() => {
if (!isValidReleaseCondition(localCondition)) {
setError(
"Invalid release condition, ensure all fields are filled out correctly.",
setLocalReleaseChannelId(undefined);
}
}}
>
{releaseChannels.length > 0 && (
<TabsList>
<TabsTrigger value="release-channels">
Release Channels
</TabsTrigger>
<TabsTrigger value="new-filter">New Filter</TabsTrigger>
</TabsList>
)}
<TabsContent value="release-channels" className="space-y-4">
<DialogHeader>
<DialogTitle>Select Release Channel</DialogTitle>
<DialogDescription>
View releases by release channel.
</DialogDescription>
</DialogHeader>
<Select
value={localReleaseChannelId}
onValueChange={(value) => {
const releaseChannel = releaseChannels.find(
(rc) => rc.id === value,
);
return;
}
onChange(localCondition);
setOpen(false);
setError(null);
}}
>
Save
</Button>
</DialogFooter>
if (releaseChannel == null) return;
setLocalReleaseChannelId(value);
setLocalCondition(releaseChannel.releaseFilter ?? undefined);
}}
>
<SelectTrigger>
<SelectValue placeholder="Select release channel..." />
</SelectTrigger>
<SelectContent>
{releaseChannels.length === 0 && (
<SelectGroup>
<SelectLabel>No release channels found</SelectLabel>
</SelectGroup>
)}
{releaseChannels.map((rc) => (
<SelectItem key={rc.id} value={rc.id}>
{rc.name}
</SelectItem>
))}
</SelectContent>
</Select>
<DialogFooter>
<Button
onClick={() => {
const releaseChannel = releaseChannels.find(
(rc) => rc.id === localReleaseChannelId,
);
if (releaseChannel == null) return;
setReleaseChannel(releaseChannel);
setOpen(false);
setError(null);
}}
disabled={localReleaseChannelId == null}
>
Save
</Button>
</DialogFooter>
</TabsContent>
<TabsContent value="new-filter" className="space-y-4">
<DialogHeader>
<DialogTitle>Edit Release Condition</DialogTitle>
<DialogDescription>
Edit the release filter, up to a depth of{" "}
{MAX_DEPTH_ALLOWED + 1}.
</DialogDescription>
</DialogHeader>
<ReleaseConditionRender
condition={localCondition ?? defaultCondition}
onChange={setLocalCondition}
/>
{releases != null && <ReleaseBadgeList releases={releases} />}
{error && <span className="text-sm text-red-600">{error}</span>}
<DialogFooter>
<Button
variant="outline"
onClick={() => {
setLocalCondition(defaultCondition);
setError(null);
}}
>
Clear
</Button>
<div className="flex-grow" />
<Button
onClick={() => {
console.log(">>> localCondition", localCondition);
if (
localCondition != null &&
!isValidReleaseCondition(localCondition)
) {
setError(
"Invalid release condition, ensure all fields are filled out correctly.",
);
return;
}
removeReleaseChannel();
onChange(localCondition);
setOpen(false);
setError(null);
}}
>
Save
</Button>
</DialogFooter>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type * as SCHEMA from "@ctrlplane/db/schema";
import type { ReleaseCondition } from "@ctrlplane/validators/releases";
import { useCallback, useMemo } from "react";
import { useRouter, useSearchParams } from "next/navigation";
Expand All @@ -17,6 +18,11 @@ export const useReleaseFilter = () => {
}
}, [urlParams]);

const releaseChannelId = useMemo<string | undefined>(
() => urlParams.get("release-channel") ?? undefined,
[urlParams],
);

const setFilter = useCallback(
(filter: ReleaseCondition | undefined) => {
if (filter == null) {
Expand All @@ -36,5 +42,36 @@ export const useReleaseFilter = () => {
[router],
);

return { filter, setFilter };
const setReleaseChannel = useCallback(
(releaseChannel: SCHEMA.ReleaseChannel) => {
const query = new URLSearchParams(window.location.search);
query.set("release-channel", releaseChannel.id);
if (releaseChannel.releaseFilter != null) {
const filterJson = LZString.compressToEncodedURIComponent(
JSON.stringify(releaseChannel.releaseFilter),
);
query.set("filter", filterJson);
}

router.replace(`?${query.toString()}`);
router.refresh();
},
[router],
);

const removeReleaseChannel = useCallback(() => {
const query = new URLSearchParams(window.location.search);
query.delete("release-channel");
query.delete("filter");
router.replace(`?${query.toString()}`);
router.refresh();
}, [router]);

return {
filter,
setFilter,
releaseChannelId,
setReleaseChannel,
removeReleaseChannel,
};
};
Loading

0 comments on commit 8cc0a72

Please sign in to comment.