Skip to content

Commit d29afc1

Browse files
committed
FS upgrades, service screens refactors
1 parent 63a7064 commit d29afc1

8 files changed

Lines changed: 109 additions & 100 deletions

File tree

frontend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
"@furystack/logging": "^8.1.4",
2727
"@furystack/rest-client-fetch": "^8.1.6",
2828
"@furystack/shades": "^13.2.1",
29-
"@furystack/shades-common-components": "^15.0.4",
30-
"@furystack/shades-mfe": "^3.0.3",
29+
"@furystack/shades-common-components": "^15.1.0",
30+
"@furystack/shades-mfe": "^3.0.5",
3131
"@furystack/utils": "^8.2.4",
3232
"common": "workspace:^"
3333
}

frontend/src/components/service-table.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { FindOptions } from '@furystack/core'
22
import { useCollectionSync } from '@furystack/entity-sync-client'
3-
import { serializeToQueryString } from '@furystack/rest'
43
import { createComponent, LocationService, Shade } from '@furystack/shades'
54
import type { ColumnFilterConfig } from '@furystack/shades-common-components'
65
import {
@@ -263,9 +262,7 @@ export const ServiceTable = Shade<ServiceTableProps>({
263262
onclick={() =>
264263
injector
265264
.getInstance(LocationService)
266-
.navigate(
267-
`/stacks/${entry.stackName}/services/${entry.id}?${serializeToQueryString({ edit: true })}`,
268-
)
265+
.navigate(`/stacks/${entry.stackName}/services/${entry.id}#configuration`)
269266
}
270267
startIcon={<Icon icon={icons.edit} size="small" />}
271268
/>

frontend/src/pages/services/service-detail/action-bar.tsx

Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,82 +5,102 @@ import type { Prerequisite, ServiceView } from 'common'
55
import { BranchSelector } from '../../../components/branch-selector.js'
66
import { ServiceStatusIndicator } from '../../../components/service-status-indicator.js'
77
import { getPrimaryAction, getSecondaryActions } from '../../../utils/service-pipeline.js'
8+
import type { TabId } from './index.js'
89

910
type ServiceDetailActionBarProps = {
1011
service: ServiceView
1112
servicePrereqs: Prerequisite[]
1213
prereqSatisfiedCount: number
1314
prereqFailedCount: number
1415
actionInProgress: string | null
16+
activeTab: TabId
1517
onRunAction: (apiAction: string) => void
18+
onEdit: () => void
1619
onDelete: () => void
1720
}
1821

1922
export const ServiceDetailActionBar = Shade<ServiceDetailActionBarProps>({
2023
customElementName: 'shade-service-detail-action-bar',
2124
render: ({ props }) => {
22-
const { service, servicePrereqs, prereqSatisfiedCount, prereqFailedCount, actionInProgress } = props
25+
const { service, servicePrereqs, prereqSatisfiedCount, prereqFailedCount, actionInProgress, activeTab } = props
2326

27+
const isEditing = activeTab === 'configuration'
2428
const primary = getPrimaryAction(service)
2529
const secondaryActions = getSecondaryActions(service)
2630

2731
return (
2832
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}>
2933
<ServiceStatusIndicator service={service} />
30-
{service.repositoryId ? (
31-
<BranchSelector
32-
serviceId={service.id}
33-
currentBranch={service.currentBranch}
34-
isCloned={service.cloneStatus === 'cloned'}
35-
/>
36-
) : null}
37-
{servicePrereqs.length > 0 ? (
38-
<Chip
39-
variant="outlined"
40-
size="small"
41-
color={
42-
prereqFailedCount > 0 ? 'error' : prereqSatisfiedCount === servicePrereqs.length ? 'success' : 'secondary'
43-
}
44-
>
45-
{prereqSatisfiedCount === servicePrereqs.length
46-
? '✓ Prerequisites OK'
47-
: `${prereqSatisfiedCount}/${servicePrereqs.length} prereqs`}
48-
</Chip>
49-
) : null}
50-
{primary.apiAction ? (
51-
<Button
52-
variant="contained"
53-
size="small"
54-
color={primary.color === 'secondary' ? undefined : primary.color}
55-
loading={!!actionInProgress}
56-
disabled={!!actionInProgress}
57-
onclick={() => props.onRunAction(primary.apiAction)}
58-
>
59-
{primary.label}
60-
</Button>
61-
) : null}
62-
{secondaryActions.map((action) => (
63-
<Button
64-
variant="outlined"
65-
size="small"
66-
color={action.color === 'secondary' ? undefined : action.color}
67-
title={action.tooltip}
68-
loading={actionInProgress === action.apiAction}
69-
disabled={!!actionInProgress}
70-
onclick={() => props.onRunAction(action.apiAction)}
71-
>
72-
{action.label}
73-
</Button>
74-
))}
75-
<Button
76-
variant="outlined"
77-
size="small"
78-
color="error"
79-
onclick={props.onDelete}
80-
startIcon={<Icon icon={icons.trash} size="small" />}
81-
>
82-
Delete
83-
</Button>
34+
{isEditing ? null : (
35+
<>
36+
{service.repositoryId ? (
37+
<BranchSelector
38+
serviceId={service.id}
39+
currentBranch={service.currentBranch}
40+
isCloned={service.cloneStatus === 'cloned'}
41+
/>
42+
) : null}
43+
{servicePrereqs.length > 0 ? (
44+
<Chip
45+
variant="outlined"
46+
size="small"
47+
color={
48+
prereqFailedCount > 0
49+
? 'error'
50+
: prereqSatisfiedCount === servicePrereqs.length
51+
? 'success'
52+
: 'secondary'
53+
}
54+
>
55+
{prereqSatisfiedCount === servicePrereqs.length
56+
? '✓ Prerequisites OK'
57+
: `${prereqSatisfiedCount}/${servicePrereqs.length} prereqs`}
58+
</Chip>
59+
) : null}
60+
{primary.apiAction ? (
61+
<Button
62+
variant="contained"
63+
size="small"
64+
color={primary.color === 'secondary' ? undefined : primary.color}
65+
loading={!!actionInProgress}
66+
disabled={!!actionInProgress}
67+
onclick={() => props.onRunAction(primary.apiAction)}
68+
>
69+
{primary.label}
70+
</Button>
71+
) : null}
72+
{secondaryActions.map((action) => (
73+
<Button
74+
variant="outlined"
75+
size="small"
76+
color={action.color === 'secondary' ? undefined : action.color}
77+
title={action.tooltip}
78+
loading={actionInProgress === action.apiAction}
79+
disabled={!!actionInProgress}
80+
onclick={() => props.onRunAction(action.apiAction)}
81+
>
82+
{action.label}
83+
</Button>
84+
))}
85+
<Button
86+
variant="outlined"
87+
size="small"
88+
onclick={props.onEdit}
89+
startIcon={<Icon icon={icons.edit} size="small" />}
90+
>
91+
Edit
92+
</Button>
93+
<Button
94+
variant="outlined"
95+
size="small"
96+
color="error"
97+
onclick={props.onDelete}
98+
startIcon={<Icon icon={icons.trash} size="small" />}
99+
>
100+
Delete
101+
</Button>
102+
</>
103+
)}
84104
</div>
85105
)
86106
},

frontend/src/pages/services/service-detail/index.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
StackDefinition,
1919
} from 'common'
2020

21-
import { stackCraftNavigate } from '../../../components/app-routes.js'
2221
import { getPrerequisiteSummary } from '../../../utils/prerequisite-summary.js'
2322
import { ServiceDetailActionBar } from './action-bar.js'
2423
import { ConfigurationTab } from './configuration-tab.js'
@@ -46,12 +45,22 @@ export const ServiceDetail = Shade<ServiceDetailProps>({
4645
render: (options) => {
4746
const { props, injector, useState } = options
4847
const locationService = injector.getInstance(LocationService)
48+
const validTabs: TabId[] = ['overview', 'logs', 'history', 'configuration']
49+
const hashValue = locationService.onLocationHashChanged.getValue().replace('#', '')
4950
const searchState = locationService.onDeserializedLocationSearchChanged.getValue()
50-
const hasEditParam = searchState.edit === true
51-
const initialTab: TabId = hasEditParam ? 'configuration' : 'overview'
52-
const [activeTab, setActiveTab] = useState<TabId>('activeTab', initialTab)
51+
const initialTab: TabId = validTabs.includes(hashValue as TabId)
52+
? (hashValue as TabId)
53+
: searchState.edit === true
54+
? 'configuration'
55+
: 'overview'
56+
const [activeTab, setActiveTabState] = useState<TabId>('activeTab', initialTab)
5357
const [isConfirmingDelete, setIsConfirmingDelete] = useState('isConfirmingDelete', false)
5458

59+
const setActiveTab = (tab: TabId) => {
60+
locationService.replace(`${window.location.pathname}#${tab}`)
61+
setActiveTabState(tab)
62+
}
63+
5564
const serviceState = useEntitySync(options, ServiceDefinition, props.serviceId)
5665
const statusState = useEntitySync(options, ServiceStatus, props.serviceId)
5766
const configState = useEntitySync(options, ServiceConfig, props.serviceId)
@@ -168,7 +177,9 @@ export const ServiceDetail = Shade<ServiceDetailProps>({
168177
prereqSatisfiedCount={prereqSatisfiedCount}
169178
prereqFailedCount={prereqFailedCount}
170179
actionInProgress={actionInProgress}
180+
activeTab={activeTab}
171181
onRunAction={(apiAction) => void runServiceAction(injector, service.id, apiAction, setActionInProgress)}
182+
onEdit={() => setActiveTab('configuration')}
172183
onDelete={() => setIsConfirmingDelete(true)}
173184
/>
174185
}
@@ -194,12 +205,7 @@ export const ServiceDetail = Shade<ServiceDetailProps>({
194205
onAction={(apiAction) =>
195206
void runServiceAction(injector, service.id, apiAction, setActionInProgress)
196207
}
197-
onViewLogs={() =>
198-
stackCraftNavigate(injector, '/stacks/:stackName/services/:serviceId/logs', {
199-
stackName: service.stackName,
200-
serviceId: service.id,
201-
})
202-
}
208+
onViewLogs={() => setActiveTab('logs')}
203209
/>
204210
),
205211
},

frontend/src/pages/services/service-detail/overview-tab.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import type { ServiceView } from 'common'
1212
import type { GitHubRepository, Prerequisite } from 'common'
1313

1414
import { StackCraftNestedRouteLink } from '../../../components/app-routes.js'
15-
import { BranchSelector } from '../../../components/branch-selector.js'
1615
import { PrerequisiteList } from '../../../components/prerequisite-list.js'
1716
import { ServicePipelineStepper } from '../../../components/service-pipeline-stepper.js'
1817

@@ -29,7 +28,7 @@ type OverviewTabProps = {
2928
prereqFailedCount: number
3029
actionInProgress: string | null
3130
onAction: (apiAction: string) => void
32-
onViewLogs: (stageId: string) => void
31+
onViewLogs: () => void
3332
}
3433

3534
export const OverviewTab = Shade<OverviewTabProps>({
@@ -90,19 +89,6 @@ export const OverviewTab = Shade<OverviewTabProps>({
9089
</span>
9190
</div>
9291
) : null}
93-
{service.repositoryId ? (
94-
<div style={{ display: 'contents' }}>
95-
<strong>Branch</strong>
96-
<span>
97-
<BranchSelector
98-
serviceId={service.id}
99-
currentBranch={service.currentBranch}
100-
isCloned={service.cloneStatus === 'cloned'}
101-
/>
102-
</span>
103-
<span />
104-
</div>
105-
) : null}
10692
<strong>Working Directory</strong>
10793
<span style={{ fontFamily: 'monospace', fontSize: cssVariableTheme.typography.fontSize.sm }}>
10894
{fullCwd ?? '(loading…)'}

frontend/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// "checkJs": true, /* Report errors in .js files. */
99
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
1010
"jsxFactory": "createComponent",
11-
"jsxFragmentFactory": "createFragment",
11+
"jsxFragmentFactory": "createComponent",
1212
"declaration": true /* Generates corresponding '.d.ts' file. */,
1313
"declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */,
1414
"sourceMap": true /* Generates corresponding '.map' file. */,

service/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"@furystack/security": "^7.0.8",
2626
"@furystack/sequelize-store": "^6.0.48",
2727
"@furystack/websocket-api": "^13.2.6",
28-
"@modelcontextprotocol/sdk": "1.28.0",
28+
"@modelcontextprotocol/sdk": "1.29.0",
2929
"common": "workspace:^",
3030
"pg": "^8.20.0",
3131
"pg-hstore": "^2.3.4",

yarn.lock

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -535,21 +535,21 @@ __metadata:
535535
languageName: node
536536
linkType: hard
537537

538-
"@furystack/shades-common-components@npm:^15.0.4":
539-
version: 15.0.4
540-
resolution: "@furystack/shades-common-components@npm:15.0.4"
538+
"@furystack/shades-common-components@npm:^15.1.0":
539+
version: 15.1.0
540+
resolution: "@furystack/shades-common-components@npm:15.1.0"
541541
dependencies:
542542
"@furystack/cache": "npm:^6.1.4"
543543
"@furystack/core": "npm:^16.0.3"
544544
"@furystack/inject": "npm:^12.0.35"
545545
"@furystack/shades": "npm:^13.2.1"
546546
"@furystack/utils": "npm:^8.2.4"
547547
path-to-regexp: "npm:^8.4.0"
548-
checksum: 10c0/f790266ef0c475b41ac605755d0badb042181b567e45ad3e7d3235fcf7342194c5017f8e948dbbfa7ff3efe64e6451423ec1b2a1d569d88f70af473e7d946e7d
548+
checksum: 10c0/02529fc3a89b037ba36161362fba17716d604d7447e7b5eae60d170f624aae08d75725b24650bbd62006cd160f27d6a21c1882a4c6021641489ac9f1d955205c
549549
languageName: node
550550
linkType: hard
551551

552-
"@furystack/shades-mfe@npm:^3.0.3":
552+
"@furystack/shades-mfe@npm:^3.0.5":
553553
version: 3.0.5
554554
resolution: "@furystack/shades-mfe@npm:3.0.5"
555555
dependencies:
@@ -680,9 +680,9 @@ __metadata:
680680
languageName: node
681681
linkType: hard
682682

683-
"@modelcontextprotocol/sdk@npm:1.28.0":
684-
version: 1.28.0
685-
resolution: "@modelcontextprotocol/sdk@npm:1.28.0"
683+
"@modelcontextprotocol/sdk@npm:1.29.0":
684+
version: 1.29.0
685+
resolution: "@modelcontextprotocol/sdk@npm:1.29.0"
686686
dependencies:
687687
"@hono/node-server": "npm:^1.19.9"
688688
ajv: "npm:^8.17.1"
@@ -709,7 +709,7 @@ __metadata:
709709
optional: true
710710
zod:
711711
optional: false
712-
checksum: 10c0/eff999884bea7302b42832b7fe172a53c83ee0afd6fa42dcda29dd503d72b6f67f3d45e9eebbcecbe52e1ee418c14574b6d30ce5a0b10f947a2a680f8bb8c8c3
712+
checksum: 10c0/7c4bc339205b1652330cd4e6b121cc859079655f2b9c0506bbb15563ba0d07924bda3d949705530532db7f4d2cb86d633dc8f92bc32803d97c7bece2ac63e29f
713713
languageName: node
714714
linkType: hard
715715

@@ -2764,8 +2764,8 @@ __metadata:
27642764
"@furystack/logging": "npm:^8.1.4"
27652765
"@furystack/rest-client-fetch": "npm:^8.1.6"
27662766
"@furystack/shades": "npm:^13.2.1"
2767-
"@furystack/shades-common-components": "npm:^15.0.4"
2768-
"@furystack/shades-mfe": "npm:^3.0.3"
2767+
"@furystack/shades-common-components": "npm:^15.1.0"
2768+
"@furystack/shades-mfe": "npm:^3.0.5"
27692769
"@furystack/utils": "npm:^8.2.4"
27702770
"@types/node": "npm:^25.5.0"
27712771
common: "workspace:^"
@@ -5073,7 +5073,7 @@ __metadata:
50735073
"@furystack/security": "npm:^7.0.8"
50745074
"@furystack/sequelize-store": "npm:^6.0.48"
50755075
"@furystack/websocket-api": "npm:^13.2.6"
5076-
"@modelcontextprotocol/sdk": "npm:1.28.0"
5076+
"@modelcontextprotocol/sdk": "npm:1.29.0"
50775077
"@types/node": "npm:^25.5.0"
50785078
common: "workspace:^"
50795079
pg: "npm:^8.20.0"

0 commit comments

Comments
 (0)