Skip to content

Commit 5793fc2

Browse files
committed
feat: [wip] easy setup wizard
1 parent bb0a939 commit 5793fc2

File tree

15 files changed

+743
-86
lines changed

15 files changed

+743
-86
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { SystemService } from '#services/system_service'
2+
import { inject } from '@adonisjs/core'
3+
import type { HttpContext } from '@adonisjs/core/http'
4+
5+
@inject()
6+
export default class EasySetupController {
7+
constructor(private systemService: SystemService) {}
8+
9+
async index({ inertia }: HttpContext) {
10+
const services = await this.systemService.getServices({ installedOnly: false })
11+
return inertia.render('easy-setup/index', {
12+
system: {
13+
services: services,
14+
},
15+
})
16+
}
17+
18+
async complete({ inertia }: HttpContext) {
19+
return inertia.render('easy-setup/complete')
20+
}
21+
}

admin/app/services/system_service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export class SystemService {
8585
friendly_name: service.friendly_name,
8686
description: service.description,
8787
installed: service.installed,
88+
installation_status: service.installation_status,
8889
status: status ? status.status : 'unknown',
8990
ui_location: service.ui_location || '',
9091
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import useDownloads, { useDownloadsProps } from '~/hooks/useDownloads'
2+
import HorizontalBarChart from './HorizontalBarChart'
3+
import { extractFileName } from '~/lib/util'
4+
import StyledSectionHeader from './StyledSectionHeader'
5+
6+
interface ActiveDownloadProps {
7+
filetype?: useDownloadsProps['filetype']
8+
withHeader?: boolean
9+
}
10+
11+
const ActiveDownloads = ({ filetype, withHeader = false }: ActiveDownloadProps) => {
12+
const { data: downloads } = useDownloads({ filetype })
13+
14+
return (
15+
<>
16+
{withHeader && <StyledSectionHeader title="Active Downloads" className="mt-12 mb-4" />}
17+
<div className="space-y-4">
18+
{downloads && downloads.length > 0 ? (
19+
downloads.map((download) => (
20+
<div className="bg-desert-white rounded-lg p-4 border border-desert-stone-light shadow-sm hover:shadow-lg transition-shadow">
21+
<HorizontalBarChart
22+
items={[
23+
{
24+
label: extractFileName(download.filepath) || download.url,
25+
value: download.progress,
26+
total: '100%',
27+
used: `${download.progress}%`,
28+
type: download.filetype,
29+
},
30+
]}
31+
/>
32+
</div>
33+
))
34+
) : (
35+
<p className="text-gray-500">No active downloads</p>
36+
)}
37+
</div>
38+
</>
39+
)
40+
}
41+
42+
export default ActiveDownloads

admin/inertia/components/Alert.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export default function Alert({
8989
: type === 'success'
9090
? 'bg-desert-olive text-desert-white border-desert-olive-dark'
9191
: type === 'info'
92-
? 'bg-desert-stone text-desert-white border-desert-stone-dark'
92+
? 'bg-desert-green text-desert-white border-desert-green-dark'
9393
: ''
9494
)
9595
return classNames(baseStyles, 'shadow-sm', ...variantStyles)
@@ -102,7 +102,7 @@ export default function Alert({
102102
: type === 'success'
103103
? 'bg-desert-olive-lighter bg-opacity-20 border-desert-olive-light'
104104
: type === 'info'
105-
? 'bg-desert-stone-lighter bg-opacity-20 border-desert-stone-light'
105+
? 'bg-desert-green bg-opacity-20 border-desert-green-light'
106106
: ''
107107
)
108108
return classNames(baseStyles, 'border shadow-sm', ...variantStyles)

admin/inertia/components/CuratedCollectionCard.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { IconCircleCheck } from '@tabler/icons-react'
66

77
export interface CuratedCollectionCardProps {
88
collection: CuratedCollectionWithStatus
9-
onClick?: (collection: CuratedCollectionWithStatus) => void
9+
onClick?: (collection: CuratedCollectionWithStatus) => void;
10+
size?: 'small' | 'large'
1011
}
1112

12-
const CuratedCollectionCard: React.FC<CuratedCollectionCardProps> = ({ collection, onClick }) => {
13+
const CuratedCollectionCard: React.FC<CuratedCollectionCardProps> = ({ collection, onClick, size = 'small' }) => {
1314
const totalSizeBytes = collection.resources?.reduce(
1415
(acc, resource) => acc + resource.size_mb * 1024 * 1024,
1516
0
@@ -18,7 +19,8 @@ const CuratedCollectionCard: React.FC<CuratedCollectionCardProps> = ({ collectio
1819
<div
1920
className={classNames(
2021
'flex flex-col bg-desert-green rounded-lg p-6 text-white border border-b-desert-green shadow-sm hover:shadow-lg transition-shadow cursor-pointer',
21-
{ 'opacity-65 cursor-not-allowed !hover:shadow-sm': collection.all_downloaded }
22+
{ 'opacity-65 cursor-not-allowed !hover:shadow-sm': collection.all_downloaded },
23+
{ 'h-56': size === 'small', 'h-80': size === 'large' }
2224
)}
2325
onClick={() => {
2426
if (collection.all_downloaded) {

admin/inertia/components/InstallActivityFeed.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ export type InstallActivityFeedProps = {
2020
message: string
2121
}>
2222
className?: string
23+
withHeader?: boolean
2324
}
2425

25-
const InstallActivityFeed: React.FC<InstallActivityFeedProps> = ({ activity, className }) => {
26+
const InstallActivityFeed: React.FC<InstallActivityFeedProps> = ({ activity, className, withHeader = false }) => {
2627
return (
2728
<div className={classNames('bg-white shadow-sm rounded-lg p-6', className)}>
28-
<h2 className="text-lg font-semibold text-gray-900">Installation Activity</h2>
29+
{withHeader && <h2 className="text-lg font-semibold text-gray-900">Installation Activity</h2>}
2930
<ul role="list" className="mt-6 space-y-6 text-desert-green">
3031
{activity.map((activityItem, activityItemIdx) => (
3132
<li key={activityItem.timestamp} className="relative flex gap-x-4">
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useEffect, useState } from 'react'
2+
import { useTransmit } from 'react-adonis-transmit'
3+
import { InstallActivityFeedProps } from '~/components/InstallActivityFeed'
4+
5+
export default function useServiceInstallationActivity() {
6+
const { subscribe } = useTransmit()
7+
const [installActivity, setInstallActivity] = useState<InstallActivityFeedProps['activity']>([])
8+
9+
useEffect(() => {
10+
const unsubscribe = subscribe('service-installation', (data: any) => {
11+
setInstallActivity((prev) => [
12+
...prev,
13+
{
14+
service_name: data.service_name ?? 'unknown',
15+
type: data.status ?? 'unknown',
16+
timestamp: new Date().toISOString(),
17+
message: data.message ?? 'No message provided',
18+
},
19+
])
20+
})
21+
22+
return () => {
23+
unsubscribe()
24+
}
25+
}, [])
26+
27+
return installActivity
28+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Head, router } from '@inertiajs/react'
2+
import AppLayout from '~/layouts/AppLayout'
3+
import StyledButton from '~/components/StyledButton'
4+
import Alert from '~/components/Alert'
5+
import useInternetStatus from '~/hooks/useInternetStatus'
6+
import useServiceInstallationActivity from '~/hooks/useServiceInstallationActivity'
7+
import InstallActivityFeed from '~/components/InstallActivityFeed'
8+
import ActiveDownloads from '~/components/ActiveDownloads'
9+
import StyledSectionHeader from '~/components/StyledSectionHeader'
10+
11+
export default function EasySetupWizardComplete() {
12+
const { isOnline } = useInternetStatus()
13+
const installActivity = useServiceInstallationActivity()
14+
15+
return (
16+
<AppLayout>
17+
<Head title="Easy Setup Wizard Complete" />
18+
{!isOnline && (
19+
<Alert
20+
title="No Internet Connection"
21+
message="It looks like you're not connected to the internet. Installing apps and downloading content will require an internet connection."
22+
type="warning"
23+
variant="solid"
24+
className="mb-8"
25+
/>
26+
)}
27+
<div className="max-w-7xl mx-auto px-4 py-8">
28+
<div className="bg-white rounded-md shadow-md p-6">
29+
<StyledSectionHeader title="App Installation Activity" className=" mb-4" />
30+
<InstallActivityFeed
31+
activity={installActivity}
32+
className="!shadow-none border-desert-stone-light border"
33+
/>
34+
<ActiveDownloads withHeader />
35+
<Alert
36+
title="Running in the Background"
37+
message='Feel free to leave this page at any time - your app installs and downloads will continue in the background!'
38+
type="info"
39+
variant="solid"
40+
className='mt-12'
41+
/>
42+
<div className="flex justify-center mt-8 pt-4 border-t border-desert-stone-light">
43+
<div className="flex space-x-4">
44+
<StyledButton onClick={() => router.visit('/home')} icon="HomeIcon">
45+
Go to Home
46+
</StyledButton>
47+
</div>
48+
</div>
49+
</div>
50+
</div>
51+
</AppLayout>
52+
)
53+
}

0 commit comments

Comments
 (0)