Skip to content

Commit 707294e

Browse files
committed
Grouping the same env var keys together
1 parent 10e714a commit 707294e

File tree

1 file changed

+110
-41
lines changed
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables

1 file changed

+110
-41
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx

Lines changed: 110 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
json,
1616
redirectDocument,
1717
} from "@remix-run/server-runtime";
18-
import { useState } from "react";
18+
import { useMemo, useState } from "react";
1919
import { typedjson, useTypedLoaderData } from "remix-typedjson";
2020
import { z } from "zod";
2121
import { InlineCode } from "~/components/code/InlineCode";
@@ -199,6 +199,43 @@ export default function Page() {
199199
const project = useProject();
200200
const environment = useEnvironment();
201201

202+
// Add isFirst and isLast to each environment variable
203+
// They're set based on if they're the first or last time that `key` has been seen in the list
204+
const groupedEnvironmentVariables = useMemo(() => {
205+
// Create a map to track occurrences of each key
206+
const keyOccurrences = new Map<string, number>();
207+
208+
// First pass: count total occurrences of each key
209+
environmentVariables.forEach((variable) => {
210+
keyOccurrences.set(variable.key, (keyOccurrences.get(variable.key) || 0) + 1);
211+
});
212+
213+
// Second pass: add isFirstTime, isLastTime, and occurrences flags
214+
const seenKeys = new Set<string>();
215+
const currentOccurrences = new Map<string, number>();
216+
217+
return environmentVariables.map((variable) => {
218+
// Track current occurrence number for this key
219+
const currentCount = (currentOccurrences.get(variable.key) || 0) + 1;
220+
currentOccurrences.set(variable.key, currentCount);
221+
222+
const totalOccurrences = keyOccurrences.get(variable.key) || 1;
223+
const isFirstTime = !seenKeys.has(variable.key);
224+
const isLastTime = currentCount === totalOccurrences;
225+
226+
if (isFirstTime) {
227+
seenKeys.add(variable.key);
228+
}
229+
230+
return {
231+
...variable,
232+
isFirstTime,
233+
isLastTime,
234+
occurences: totalOccurrences,
235+
};
236+
});
237+
}, [environmentVariables]);
238+
202239
return (
203240
<PageContainer>
204241
<NavBar>
@@ -245,47 +282,79 @@ export default function Page() {
245282
</TableRow>
246283
</TableHeader>
247284
<TableBody>
248-
{environmentVariables.length > 0 ? (
249-
environmentVariables.map((variable) => (
250-
<TableRow key={`${variable.id}-${variable.environment.id}`}>
251-
<TableCell>
252-
<CopyableText value={variable.key} className="font-mono" />
253-
</TableCell>
254-
<TableCell>
255-
{variable.isSecret ? (
256-
<SimpleTooltip
257-
button={
258-
<div className="flex items-center gap-x-1.5">
259-
<LockClosedIcon className="size-3 text-text-dimmed" />
260-
<span className="text-sm text-text-dimmed">Secret</span>
261-
</div>
262-
}
263-
content="This variable is secret and cannot be revealed."
264-
/>
265-
) : (
266-
<ClipboardField
267-
secure={!revealAll}
268-
value={variable.value}
269-
variant={"secondary/small"}
270-
fullWidth={true}
271-
/>
272-
)}
273-
</TableCell>
274-
275-
<TableCell>
276-
<EnvironmentCombo environment={variable.environment} className="text-sm" />
277-
</TableCell>
278-
<TableCellMenu
279-
isSticky
280-
hiddenButtons={
281-
<>
282-
<EditEnvironmentVariablePanel variable={variable} revealAll={revealAll} />
283-
<DeleteEnvironmentVariableButton variable={variable} />
284-
</>
285+
{groupedEnvironmentVariables.length > 0 ? (
286+
groupedEnvironmentVariables.map((variable) => {
287+
let cellClassName = "";
288+
let borderedCellClassName = "";
289+
290+
if (variable.occurences > 1) {
291+
cellClassName = "py-1";
292+
borderedCellClassName =
293+
"relative after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright";
294+
if (variable.isLastTime) {
295+
cellClassName = "pt-1 pb-2";
296+
borderedCellClassName = "";
297+
} else if (variable.isFirstTime) {
298+
cellClassName = "pt-2 pb-1";
299+
}
300+
} else {
301+
cellClassName = "py-2";
302+
}
303+
304+
return (
305+
<TableRow
306+
key={`${variable.id}-${variable.environment.id}`}
307+
className={
308+
variable.isLastTime ? "after:bg-charcoal-600" : "after:bg-transparent"
285309
}
286-
/>
287-
</TableRow>
288-
))
310+
>
311+
<TableCell className={cellClassName}>
312+
{variable.isFirstTime ? (
313+
<CopyableText value={variable.key} className="font-mono" />
314+
) : null}
315+
</TableCell>
316+
<TableCell
317+
className={cn(cellClassName, borderedCellClassName, "after:left-3")}
318+
>
319+
{variable.isSecret ? (
320+
<SimpleTooltip
321+
button={
322+
<div className="flex items-center gap-x-1.5">
323+
<LockClosedIcon className="size-3 text-text-dimmed" />
324+
<span className="text-sm text-text-dimmed">Secret</span>
325+
</div>
326+
}
327+
content="This variable is secret and cannot be revealed."
328+
/>
329+
) : (
330+
<ClipboardField
331+
secure={!revealAll}
332+
value={variable.value}
333+
variant={"secondary/small"}
334+
fullWidth={true}
335+
/>
336+
)}
337+
</TableCell>
338+
339+
<TableCell className={cn(cellClassName, borderedCellClassName)}>
340+
<EnvironmentCombo environment={variable.environment} className="text-sm" />
341+
</TableCell>
342+
<TableCellMenu
343+
className={cn(cellClassName, borderedCellClassName)}
344+
isSticky
345+
hiddenButtons={
346+
<>
347+
<EditEnvironmentVariablePanel
348+
variable={variable}
349+
revealAll={revealAll}
350+
/>
351+
<DeleteEnvironmentVariableButton variable={variable} />
352+
</>
353+
}
354+
/>
355+
</TableRow>
356+
);
357+
})
289358
) : (
290359
<TableRow>
291360
<TableCell colSpan={4}>

0 commit comments

Comments
 (0)