1- import type { ActionFunctionArgs , LoaderFunctionArgs } from "@remix-run/node" ;
2- import { json , redirect } from "@remix-run/node" ;
1+ import { type ActionFunctionArgs , type LoaderFunctionArgs , json , redirect } from "@remix-run/node" ;
32import { fromPromise } from "neverthrow" ;
43import { Form , useActionData , useNavigation } from "@remix-run/react" ;
54import { typedjson , useTypedLoaderData } from "remix-typedjson" ;
65import { z } from "zod" ;
76import { DialogClose } from "@radix-ui/react-dialog" ;
8- import { SlackIcon } from "@trigger.dev/companyicons" ;
97import { TrashIcon } from "@heroicons/react/20/solid" ;
108import { Button } from "~/components/primitives/Buttons" ;
9+ import { DateTime } from "~/components/primitives/DateTime" ;
1110import {
1211 Dialog ,
1312 DialogContent ,
@@ -17,8 +16,14 @@ import {
1716 DialogTrigger ,
1817} from "~/components/primitives/Dialog" ;
1918import { FormButtons } from "~/components/primitives/FormButtons" ;
20- import { Header1 } from "~/components/primitives/Headers" ;
21- import { PageBody , PageContainer } from "~/components/layout/AppLayout" ;
19+ import { Header2 , Header3 } from "~/components/primitives/Headers" ;
20+ import { Hint } from "~/components/primitives/Hint" ;
21+ import {
22+ MainHorizontallyCenteredContainer ,
23+ PageBody ,
24+ PageContainer ,
25+ } from "~/components/layout/AppLayout" ;
26+ import { NavBar , PageTitle } from "~/components/primitives/PageHeader" ;
2227import { Paragraph } from "~/components/primitives/Paragraph" ;
2328import {
2429 Table ,
@@ -34,18 +39,6 @@ import { requireOrganization } from "~/services/org.server";
3439import { OrganizationParamsSchema , organizationSettingsPath } from "~/utils/pathBuilder" ;
3540import { logger } from "~/services/logger.server" ;
3641
37- function formatDate ( date : Date ) : string {
38- return new Intl . DateTimeFormat ( "en-US" , {
39- month : "short" ,
40- day : "numeric" ,
41- year : "numeric" ,
42- hour : "numeric" ,
43- minute : "2-digit" ,
44- second : "2-digit" ,
45- hour12 : true ,
46- } ) . format ( date ) ;
47- }
48-
4942export const loader = async ( { request, params } : LoaderFunctionArgs ) => {
5043 const { organizationSlug } = OrganizationParamsSchema . parse ( params ) ;
5144 const { organization } = await requireOrganization ( request , organizationSlug ) ;
@@ -187,8 +180,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
187180} ;
188181
189182export default function SlackIntegrationPage ( ) {
190- const { slackIntegration, alertChannels, teamName } =
191- useTypedLoaderData < typeof loader > ( ) ;
183+ const { slackIntegration, alertChannels, teamName } = useTypedLoaderData < typeof loader > ( ) ;
192184 const actionData = useActionData < typeof action > ( ) ;
193185 const navigation = useNavigation ( ) ;
194186 const isUninstalling =
@@ -197,129 +189,155 @@ export default function SlackIntegrationPage() {
197189 if ( ! slackIntegration ) {
198190 return (
199191 < PageContainer >
192+ < NavBar >
193+ < PageTitle title = "Slack integration" />
194+ </ NavBar >
200195 < PageBody >
201- < div className = "flex flex-col items-center justify-center py-8" >
202- < Header1 > No Slack Integration Found</ Header1 >
203- < Paragraph className = "mt-2 text-center text-text-dimmed" >
204- This organization doesn't have a Slack integration configured. You can connect Slack
205- when setting up alert channels in your project settings.
206- </ Paragraph >
207- </ div >
196+ < MainHorizontallyCenteredContainer >
197+ < div className = "flex h-full items-center justify-center" >
198+ < div className = "text-center" >
199+ < Header3 className = "mb-2" > No Slack integration found</ Header3 >
200+ < Paragraph variant = "small" >
201+ This organization doesn't have a Slack integration configured. You can connect
202+ Slack when setting up alert channels in your project settings.
203+ </ Paragraph >
204+ </ div >
205+ </ div >
206+ </ MainHorizontallyCenteredContainer >
208207 </ PageBody >
209208 </ PageContainer >
210209 ) ;
211210 }
212211
213212 return (
214213 < PageContainer >
214+ < NavBar >
215+ < PageTitle title = "Slack integration" />
216+ </ NavBar >
215217 < PageBody >
216- < div className = "mb-8" >
217- < Header1 > Slack Integration</ Header1 >
218- < Paragraph className = "mt-2 text-text-dimmed" >
219- Manage your organization's Slack integration and connected alert channels.
220- </ Paragraph >
221- </ div >
222-
223- { /* Integration Info Section */ }
224- < div className = "mb-8 rounded-lg border border-grid-bright bg-background-bright p-6" >
225- < div className = "flex items-center justify-between" >
218+ < MainHorizontallyCenteredContainer >
219+ < div className = "flex flex-col gap-6" >
226220 < div >
227- < h2 className = "text-lg font-medium text-text-bright" > Integration Details</ h2 >
228- < div className = "mt-2 space-y-1 text-sm text-text-dimmed" >
221+ < div className = "mb-3 border-b border-grid-dimmed pb-3" >
222+ < Header2 > Integration details</ Header2 >
223+ </ div >
224+ < div className = "flex flex-col gap-1" >
229225 { teamName && (
230- < div >
231- < span className = "font-medium" > Slack Workspace:</ span > { teamName }
232- </ div >
226+ < Paragraph variant = "small" >
227+ < span className = "text-text-dimmed" > Workspace:</ span > { " " }
228+ < span className = "text-text-bright" > { teamName } </ span >
229+ </ Paragraph >
233230 ) }
234- < div >
235- < span className = "font-medium" > Installed:</ span > { " " }
236- { formatDate ( new Date ( slackIntegration . createdAt ) ) }
237- </ div >
231+ < Paragraph variant = "small" >
232+ < span className = "text-text-dimmed" > Installed:</ span > { " " }
233+ < span className = "text-text-bright" >
234+ < DateTime date = { slackIntegration . createdAt } />
235+ </ span >
236+ </ Paragraph >
238237 </ div >
239238 </ div >
240- < div className = "flex flex-col items-end gap-2" >
241- < Dialog >
242- < DialogTrigger asChild >
243- < Button variant = "danger/medium" LeadingIcon = { TrashIcon } disabled = { isUninstalling } >
244- Remove Integration
245- </ Button >
246- </ DialogTrigger >
247- < DialogContent >
248- < DialogHeader >
249- < DialogTitle > Remove Slack Integration</ DialogTitle >
250- </ DialogHeader >
251- < DialogDescription >
252- This will remove the Slack integration and disable all connected alert channels.
253- This action cannot be undone.
254- </ DialogDescription >
255- < FormButtons
256- confirmButton = {
257- < Form method = "post" >
258- < input type = "hidden" name = "intent" value = "uninstall" />
239+
240+ < div >
241+ < div className = "mb-3 border-b border-grid-dimmed pb-3" >
242+ < Header3 >
243+ Connected alert channels
244+ < span className = "ml-1 text-text-dimmed" > ({ alertChannels . length } )</ span >
245+ </ Header3 >
246+ </ div >
247+ { alertChannels . length === 0 ? (
248+ < Paragraph variant = "small" className = "text-text-dimmed" >
249+ No alert channels are currently connected to this Slack integration.
250+ </ Paragraph >
251+ ) : (
252+ < Table >
253+ < TableHeader >
254+ < TableRow >
255+ < TableHeaderCell > Channel</ TableHeaderCell >
256+ < TableHeaderCell > Project</ TableHeaderCell >
257+ < TableHeaderCell > Status</ TableHeaderCell >
258+ < TableHeaderCell > Created</ TableHeaderCell >
259+ </ TableRow >
260+ </ TableHeader >
261+ < TableBody >
262+ { alertChannels . map ( ( channel ) => (
263+ < TableRow key = { channel . id } >
264+ < TableCell > { channel . name } </ TableCell >
265+ < TableCell > { channel . project . name } </ TableCell >
266+ < TableCell >
267+ < EnabledStatus enabled = { channel . enabled } />
268+ </ TableCell >
269+ < TableCell >
270+ < DateTime date = { channel . createdAt } />
271+ </ TableCell >
272+ </ TableRow >
273+ ) ) }
274+ </ TableBody >
275+ </ Table >
276+ ) }
277+ </ div >
278+
279+ < div >
280+ < Header2 spacing > Danger zone</ Header2 >
281+ < div className = "w-full rounded-sm border border-rose-500/40 p-4" >
282+ < Header3 spacing > Remove integration</ Header3 >
283+ < Hint >
284+ This will remove the Slack integration and disable all connected alert channels.
285+ This action cannot be undone.
286+ </ Hint >
287+ { actionData ?. error && (
288+ < Paragraph variant = "small" className = "mt-2 text-error" >
289+ { actionData . error }
290+ </ Paragraph >
291+ ) }
292+ < FormButtons
293+ className = "mt-2"
294+ confirmButton = {
295+ < Dialog >
296+ < DialogTrigger asChild >
259297 < Button
260- variant = "danger/medium "
298+ variant = "danger/small "
261299 LeadingIcon = { TrashIcon }
262- type = "submit"
263300 disabled = { isUninstalling }
264301 >
265- { isUninstalling ? "Removing..." : " Remove Integration" }
302+ Remove integration
266303 </ Button >
267- </ Form >
268- }
269- cancelButton = {
270- < DialogClose asChild >
271- < Button variant = "tertiary/medium" > Cancel</ Button >
272- </ DialogClose >
273- }
274- />
275- </ DialogContent >
276- </ Dialog >
277- { actionData ?. error && (
278- < Paragraph variant = "small" className = "text-error" >
279- { actionData . error }
280- </ Paragraph >
281- ) }
304+ </ DialogTrigger >
305+ < DialogContent >
306+ < DialogHeader >
307+ < DialogTitle > Remove Slack integration</ DialogTitle >
308+ </ DialogHeader >
309+ < DialogDescription className = "mb-2" >
310+ This will remove the Slack integration and disable all connected alert
311+ channels. This action cannot be undone.
312+ </ DialogDescription >
313+ < FormButtons
314+ confirmButton = {
315+ < Form method = "post" >
316+ < input type = "hidden" name = "intent" value = "uninstall" />
317+ < Button
318+ variant = "danger/medium"
319+ LeadingIcon = { TrashIcon }
320+ type = "submit"
321+ disabled = { isUninstalling }
322+ >
323+ { isUninstalling ? "Removing…" : "Remove integration" }
324+ </ Button >
325+ </ Form >
326+ }
327+ cancelButton = {
328+ < DialogClose asChild >
329+ < Button variant = "tertiary/medium" > Cancel</ Button >
330+ </ DialogClose >
331+ }
332+ />
333+ </ DialogContent >
334+ </ Dialog >
335+ }
336+ />
337+ </ div >
282338 </ div >
283339 </ div >
284- </ div >
285-
286- { /* Connected Alert Channels Section */ }
287- < div >
288- < h2 className = "mb-4 text-lg font-medium text-text-bright" >
289- Connected Alert Channels ({ alertChannels . length } )
290- </ h2 >
291-
292- { alertChannels . length === 0 ? (
293- < div className = "rounded-lg border border-grid-bright bg-background-bright p-6 text-center" >
294- < Paragraph className = "text-text-dimmed" >
295- No alert channels are currently connected to this Slack integration.
296- </ Paragraph >
297- </ div >
298- ) : (
299- < Table >
300- < TableHeader >
301- < TableRow >
302- < TableHeaderCell > Channel Name</ TableHeaderCell >
303- < TableHeaderCell > Project</ TableHeaderCell >
304- < TableHeaderCell > Status</ TableHeaderCell >
305- < TableHeaderCell > Created</ TableHeaderCell >
306- </ TableRow >
307- </ TableHeader >
308- < TableBody >
309- { alertChannels . map ( ( channel ) => (
310- < TableRow key = { channel . id } >
311- < TableCell > { channel . name } </ TableCell >
312- < TableCell > { channel . project . name } </ TableCell >
313- < TableCell >
314- < EnabledStatus enabled = { channel . enabled } />
315- </ TableCell >
316- < TableCell > { formatDate ( new Date ( channel . createdAt ) ) } </ TableCell >
317- </ TableRow >
318- ) ) }
319- </ TableBody >
320- </ Table >
321- ) }
322- </ div >
340+ </ MainHorizontallyCenteredContainer >
323341 </ PageBody >
324342 </ PageContainer >
325343 ) ;
0 commit comments