@picked/svelte 0.0.1-dev.1770787109735 → 0.0.1-dev.1771901344537
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/field.d.ts +2 -5
- package/dist/hooks/field.js +1 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.js +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +107 -67
- package/dist/plugins/devtools/PickedIcon.svelte +19 -0
- package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +2 -1
- package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +26 -20
- package/dist/plugins/devtools/components/Tabs.svelte +20 -7
- package/dist/plugins/devtools/experience.js +1 -1
- package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +18 -14
- package/dist/plugins/devtools/screens/events/EventListItem.svelte +128 -0
- package/dist/plugins/devtools/screens/events/EventsListScreen.svelte +18 -0
- package/dist/plugins/devtools/screens/events/EventsTable.svelte +76 -0
- package/dist/plugins/devtools/screens/experience-instances/ExperienceInstancesListScreen.svelte +1 -3
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +34 -33
- package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte +1 -3
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +20 -71
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +175 -0
- package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +15 -13
- package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +22 -16
- package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +15 -13
- package/dist/plugins/devtools/screens/project/ProjectScreen.svelte +47 -1
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +25 -63
- package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +46 -65
- package/dist/plugins/devtools/ui/actions/Button.svelte +175 -0
- package/dist/plugins/devtools/ui/actions/CopyButton.svelte +88 -0
- package/dist/plugins/devtools/ui/data/Badge.svelte +62 -0
- package/dist/plugins/devtools/ui/data/CodeBlock.svelte +38 -0
- package/dist/plugins/devtools/ui/data/DataTable.svelte +144 -0
- package/dist/plugins/devtools/ui/data/EmptyState.svelte +26 -0
- package/dist/plugins/devtools/ui/data/ExpandableRow.svelte +66 -0
- package/dist/plugins/devtools/ui/data/VisualizationContainer.svelte +55 -0
- package/dist/plugins/devtools/ui/feedback/Banner.svelte +116 -0
- package/dist/plugins/devtools/ui/feedback/Spinner.svelte +79 -0
- package/dist/plugins/devtools/ui/forms/Checkbox.svelte +139 -0
- package/dist/plugins/devtools/ui/forms/RadioGroup.svelte +218 -0
- package/dist/plugins/devtools/ui/forms/Select.svelte +137 -0
- package/dist/plugins/devtools/ui/forms/Switch.svelte +158 -0
- package/dist/plugins/devtools/ui/forms/TextField.svelte +125 -0
- package/dist/plugins/devtools/ui/index.d.ts +22 -0
- package/dist/plugins/devtools/ui/index.js +1 -0
- package/dist/plugins/devtools/ui/layout/Card.svelte +55 -0
- package/dist/plugins/devtools/ui/layout/Divider.svelte +34 -0
- package/dist/plugins/devtools/ui/layout/Spacer.svelte +11 -0
- package/dist/plugins/devtools/ui/layout/Stack.svelte +129 -0
- package/dist/plugins/devtools/ui/overlays/Tooltip.svelte +90 -0
- package/dist/plugins/devtools/ui/tokens.css +144 -0
- package/dist/plugins/devtools/ui/tokens.css.js +1 -0
- package/dist/plugins/devtools/ui/typography/Heading.svelte +55 -0
- package/dist/plugins/devtools/ui/typography/Text.svelte +93 -0
- package/package.json +17 -2
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { combine, createStore, createEvent, sample } from 'effector'
|
|
3
|
+
import { getPicked } from '../../../../hooks'
|
|
4
|
+
import EventListItem from './EventListItem.svelte'
|
|
5
|
+
import { Card, DataTable, EmptyState, Stack } from '../../ui'
|
|
6
|
+
import type { EventHistoryEntry } from '@picked/client'
|
|
7
|
+
|
|
8
|
+
const { events } = getPicked()
|
|
9
|
+
|
|
10
|
+
let expandedRowIds = new Set<number>()
|
|
11
|
+
|
|
12
|
+
function toggleRow(eventId: number) {
|
|
13
|
+
const newSet = new Set(expandedRowIds)
|
|
14
|
+
if (newSet.has(eventId)) {
|
|
15
|
+
newSet.delete(eventId)
|
|
16
|
+
} else {
|
|
17
|
+
newSet.add(eventId)
|
|
18
|
+
}
|
|
19
|
+
expandedRowIds = newSet
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const sortDirectionStore = createStore<'asc' | 'desc'>('desc')
|
|
23
|
+
const toggleSortDirection = createEvent<void>()
|
|
24
|
+
const oppositeSortDirection: Record<'asc' | 'desc', 'asc' | 'desc'> = { asc: 'desc', desc: 'asc' }
|
|
25
|
+
|
|
26
|
+
sample({
|
|
27
|
+
clock: toggleSortDirection,
|
|
28
|
+
source: sortDirectionStore,
|
|
29
|
+
fn: (dir) => oppositeSortDirection[dir],
|
|
30
|
+
target: sortDirectionStore,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
function sortEvents(history: EventHistoryEntry[], sort: 'asc' | 'desc'): EventHistoryEntry[] {
|
|
34
|
+
return [...history].sort((a, b) => {
|
|
35
|
+
const diff = a.at.getTime() - b.at.getTime()
|
|
36
|
+
return sort === 'asc' ? diff : -diff
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const sortedEvents = combine(events, sortDirectionStore, sortEvents)
|
|
41
|
+
|
|
42
|
+
const columns = [
|
|
43
|
+
{ key: 'time', header: 'Time', sortable: true },
|
|
44
|
+
{ key: 'eventType', header: 'Event Type' },
|
|
45
|
+
{ key: 'experience', header: 'Experience' },
|
|
46
|
+
{ key: 'actions', header: '' },
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
function handleSort() {
|
|
50
|
+
toggleSortDirection()
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<Stack padding="medium">
|
|
55
|
+
<Card sectioned>
|
|
56
|
+
<DataTable
|
|
57
|
+
columns={columns}
|
|
58
|
+
rows={$sortedEvents}
|
|
59
|
+
sortKey="time"
|
|
60
|
+
sortDirection={$sortDirectionStore}
|
|
61
|
+
onSort={handleSort}
|
|
62
|
+
>
|
|
63
|
+
{#snippet row(item)}
|
|
64
|
+
{@const event = item}
|
|
65
|
+
<EventListItem
|
|
66
|
+
{event}
|
|
67
|
+
isExpanded={expandedRowIds.has(event.id)}
|
|
68
|
+
onToggle={() => toggleRow(event.id)}
|
|
69
|
+
/>
|
|
70
|
+
{/snippet}
|
|
71
|
+
{#snippet empty()}
|
|
72
|
+
<EmptyState message="No events yet. Interact with the page to see events." />
|
|
73
|
+
{/snippet}
|
|
74
|
+
</DataTable>
|
|
75
|
+
</Card>
|
|
76
|
+
</Stack>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ExperienceStore } from '@picked/client'
|
|
3
3
|
import { getPicked } from '../../../../hooks'
|
|
4
|
+
import { Button, Card, Text, CodeBlock, Stack } from '../../ui'
|
|
4
5
|
|
|
5
6
|
const client = getPicked()
|
|
6
7
|
export let experience: ExperienceStore
|
|
@@ -8,39 +9,39 @@
|
|
|
8
9
|
$: config = experience.configStore
|
|
9
10
|
</script>
|
|
10
11
|
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
12
|
+
<Stack direction="vertical" gap="medium" padding="medium">
|
|
13
|
+
<Stack direction="horizontal" gap="small">
|
|
14
|
+
<Button variant="primary" size="small" onclick={() => client.launchExperienceById(experience.id)}>
|
|
15
|
+
Launch experience
|
|
16
|
+
</Button>
|
|
17
|
+
</Stack>
|
|
15
18
|
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
<Card sectioned>
|
|
20
|
+
<Stack direction="vertical" gap="medium" padding="medium">
|
|
21
|
+
<div>
|
|
22
|
+
<Text weight="semibold">Type</Text>
|
|
23
|
+
<Text>{$config.type}</Text>
|
|
24
|
+
</div>
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
<div>
|
|
27
|
+
<Text weight="semibold">Target selectors</Text>
|
|
28
|
+
<ul>
|
|
29
|
+
{#if $config.targetSelectors.length > 0}
|
|
30
|
+
{#each $config.targetSelectors as selector (selector)}
|
|
31
|
+
<li>
|
|
32
|
+
<CodeBlock code="{selector.selector} || {selector.placement}" />
|
|
33
|
+
</li>
|
|
34
|
+
{/each}
|
|
35
|
+
{:else}
|
|
36
|
+
<li><Text color="subdued">(none)</Text></li>
|
|
37
|
+
{/if}
|
|
38
|
+
</ul>
|
|
39
|
+
</div>
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.container {
|
|
44
|
-
padding: 0.5em;
|
|
45
|
-
}
|
|
46
|
-
</style>
|
|
41
|
+
<div>
|
|
42
|
+
<Text weight="semibold">Decision logic</Text>
|
|
43
|
+
<CodeBlock code={JSON.stringify($config.decisionLogic, null, 2)} />
|
|
44
|
+
</div>
|
|
45
|
+
</Stack>
|
|
46
|
+
</Card>
|
|
47
|
+
</Stack>
|
package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte
CHANGED
|
@@ -10,6 +10,4 @@
|
|
|
10
10
|
$: experienceInstances = client.experienceInstances.getByExperienceId(experience.id)
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
|
-
<
|
|
14
|
-
<ExperienceInstancesTable experienceInstances={$experienceInstances} />
|
|
15
|
-
</div>
|
|
13
|
+
<ExperienceInstancesTable experienceInstances={$experienceInstances} />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as d3 from 'd3'
|
|
3
3
|
import type { ExperienceEdge, ExperienceNode, ExperienceStore } from '@picked/client'
|
|
4
|
+
import { Badge, Button, Spinner, Stack, Text, VisualizationContainer } from '../../ui'
|
|
4
5
|
|
|
5
6
|
export let experience: ExperienceStore
|
|
6
7
|
|
|
@@ -237,74 +238,22 @@
|
|
|
237
238
|
}
|
|
238
239
|
</script>
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
<svg bind:this={svgEl} class="tree-svg"></svg>
|
|
260
|
-
</div>
|
|
261
|
-
{/if}
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
<style>
|
|
265
|
-
.graph {
|
|
266
|
-
border: 1px solid lightgray;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.loading-container,
|
|
270
|
-
.error-container {
|
|
271
|
-
display: flex;
|
|
272
|
-
height: 100%;
|
|
273
|
-
align-items: center;
|
|
274
|
-
justify-content: center;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.error-container {
|
|
278
|
-
color: #ef4444;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.tree-container {
|
|
282
|
-
position: relative;
|
|
283
|
-
height: 100%;
|
|
284
|
-
width: 100%;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.controls {
|
|
288
|
-
position: absolute;
|
|
289
|
-
right: 1rem;
|
|
290
|
-
top: 1rem;
|
|
291
|
-
z-index: 10;
|
|
292
|
-
display: flex;
|
|
293
|
-
gap: 0.5rem;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
.zoom-level {
|
|
297
|
-
display: flex;
|
|
298
|
-
align-items: center;
|
|
299
|
-
border-radius: 0.25rem;
|
|
300
|
-
background-color: rgba(255, 255, 255, 0.8);
|
|
301
|
-
padding: 0.25rem 0.5rem;
|
|
302
|
-
font-size: 0.75rem;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.tree-svg {
|
|
306
|
-
height: 100%;
|
|
307
|
-
width: 100%;
|
|
308
|
-
background-color: white;
|
|
309
|
-
}
|
|
310
|
-
</style>
|
|
241
|
+
{#if loading}
|
|
242
|
+
<Stack direction="horizontal" align="center" justify="center" fill>
|
|
243
|
+
<Spinner label="Loading visualization" />
|
|
244
|
+
</Stack>
|
|
245
|
+
{:else if error}
|
|
246
|
+
<Stack direction="horizontal" align="center" justify="center" fill>
|
|
247
|
+
<Text color="error">{error}</Text>
|
|
248
|
+
</Stack>
|
|
249
|
+
{:else}
|
|
250
|
+
<VisualizationContainer>
|
|
251
|
+
{#snippet controls()}
|
|
252
|
+
<Button variant="secondary" size="small" onclick={handleZoomIn}>Zoom in</Button>
|
|
253
|
+
<Button variant="secondary" size="small" onclick={handleZoomOut}>Zoom out</Button>
|
|
254
|
+
<Button variant="secondary" size="small" onclick={handleReset}>Reset</Button>
|
|
255
|
+
<Badge status="default" size="small">{Math.round(zoom * 100)}%</Badge>
|
|
256
|
+
{/snippet}
|
|
257
|
+
<svg bind:this={svgEl}></svg>
|
|
258
|
+
</VisualizationContainer>
|
|
259
|
+
{/if}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createEvent, createStore } from 'effector'
|
|
3
|
+
import type { FeatureBucket } from '@picked/client'
|
|
4
|
+
import { getPicked, useUnit } from '../../../../hooks'
|
|
5
|
+
import { Button, Card, CodeBlock, CopyButton, DataTable, EmptyState, Stack, Text } from '../../ui'
|
|
6
|
+
|
|
7
|
+
interface FeatureSelection {
|
|
8
|
+
included: boolean
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { features: { listStore: featuresStore } } = getPicked()
|
|
13
|
+
const featuresList = useUnit(featuresStore)
|
|
14
|
+
|
|
15
|
+
const initSelections = createEvent<FeatureBucket[]>()
|
|
16
|
+
const featureToggled = createEvent<string>()
|
|
17
|
+
const valueChanged = createEvent<{ id: string; value: string }>()
|
|
18
|
+
|
|
19
|
+
const selectionsStore = createStore<Record<string, FeatureSelection>>({})
|
|
20
|
+
.on(initSelections, (state, feats) => {
|
|
21
|
+
const next = { ...state }
|
|
22
|
+
feats.forEach((feature) => {
|
|
23
|
+
if (!(feature.id in next)) {
|
|
24
|
+
const bucket = feature.isResolved ? feature.getBucket().bucket : null
|
|
25
|
+
next[feature.id] = {
|
|
26
|
+
included: false,
|
|
27
|
+
value: bucket !== null ? String(bucket) : 'true',
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
return next
|
|
32
|
+
})
|
|
33
|
+
.on(featureToggled, (state, id) => ({
|
|
34
|
+
...state,
|
|
35
|
+
[id]: { ...state[id], included: !state[id].included },
|
|
36
|
+
}))
|
|
37
|
+
.on(valueChanged, (state, { id, value }) => ({
|
|
38
|
+
...state,
|
|
39
|
+
[id]: { ...state[id], value },
|
|
40
|
+
}))
|
|
41
|
+
|
|
42
|
+
const selections = useUnit(selectionsStore)
|
|
43
|
+
|
|
44
|
+
$effect(() => {
|
|
45
|
+
initSelections($featuresList)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
function getBucketValue(feature: FeatureBucket): string | null {
|
|
49
|
+
return feature.isResolved ? String(feature.getBucket().bucket) : null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isBooleanFeature(feature: FeatureBucket): boolean {
|
|
53
|
+
if (!feature.isResolved) return true
|
|
54
|
+
const bucket = getBucketValue(feature)
|
|
55
|
+
return bucket === 'true' || bucket === 'false'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const generatedUrl = $derived.by(() => {
|
|
59
|
+
const url = new URL(window.location.href)
|
|
60
|
+
url.searchParams.delete('picked-feature')
|
|
61
|
+
const featureParams = $featuresList
|
|
62
|
+
.filter((feature) => $selections[feature.id]?.included)
|
|
63
|
+
.map((feature) => `picked-feature=${feature.id}:${$selections[feature.id].value}`)
|
|
64
|
+
const base = url.toString()
|
|
65
|
+
if (featureParams.length === 0) return base
|
|
66
|
+
const separator = url.search ? '&' : '?'
|
|
67
|
+
return base + separator + featureParams.join('&')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const hasSelections = $derived(Object.values($selections).some((s) => s.included))
|
|
71
|
+
|
|
72
|
+
function refreshPage() {
|
|
73
|
+
window.location.href = generatedUrl
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const columns = [
|
|
77
|
+
{ key: 'include', header: 'Include' },
|
|
78
|
+
{ key: 'feature', header: 'Feature' },
|
|
79
|
+
{ key: 'currentValue', header: 'Current Value' },
|
|
80
|
+
{ key: 'overrideValue', header: 'Override Value' },
|
|
81
|
+
]
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<Stack direction="vertical" gap="medium" padding="medium">
|
|
85
|
+
<Text size="caption" color="subdued">
|
|
86
|
+
Override feature flags via URL parameters. Select features below, choose values, then copy the
|
|
87
|
+
generated URL. Recipients will see the overridden values on page load.
|
|
88
|
+
</Text>
|
|
89
|
+
|
|
90
|
+
<Card sectioned>
|
|
91
|
+
<DataTable columns={columns} rows={$featuresList}>
|
|
92
|
+
{#snippet row(item)}
|
|
93
|
+
{@const feature = item}
|
|
94
|
+
{@const selection = $selections[feature.id]}
|
|
95
|
+
{@const currentValue = getBucketValue(feature)}
|
|
96
|
+
{@const isBoolean = isBooleanFeature(feature)}
|
|
97
|
+
{#if selection}
|
|
98
|
+
<tr class:selected={selection.included}>
|
|
99
|
+
<td class="include-cell">
|
|
100
|
+
<input
|
|
101
|
+
type="checkbox"
|
|
102
|
+
checked={selection.included}
|
|
103
|
+
onchange={() => featureToggled(feature.id)}
|
|
104
|
+
aria-label="Include {feature.name} in URL"
|
|
105
|
+
/>
|
|
106
|
+
</td>
|
|
107
|
+
<td>
|
|
108
|
+
<Text size="small" weight="medium">{feature.name}</Text>
|
|
109
|
+
<CodeBlock code={feature.id + ':' + feature.key} inline />
|
|
110
|
+
</td>
|
|
111
|
+
<td>
|
|
112
|
+
{#if currentValue !== null}
|
|
113
|
+
<CodeBlock code={currentValue} inline />
|
|
114
|
+
{:else}
|
|
115
|
+
<Button variant="ghost" size="small" onclick={() => feature.getBucket()}>
|
|
116
|
+
Click to resolve
|
|
117
|
+
</Button>
|
|
118
|
+
{/if}
|
|
119
|
+
</td>
|
|
120
|
+
<td>
|
|
121
|
+
{#if isBoolean}
|
|
122
|
+
<select
|
|
123
|
+
class="inline-select"
|
|
124
|
+
value={selection.value}
|
|
125
|
+
onchange={(e) => valueChanged({ id: feature.id, value: e.currentTarget.value })}
|
|
126
|
+
disabled={!selection.included}
|
|
127
|
+
>
|
|
128
|
+
<option value="true">true</option>
|
|
129
|
+
<option value="false">false</option>
|
|
130
|
+
</select>
|
|
131
|
+
{:else}
|
|
132
|
+
<input
|
|
133
|
+
class="inline-input"
|
|
134
|
+
type="text"
|
|
135
|
+
value={selection.value}
|
|
136
|
+
oninput={(e) => valueChanged({ id: feature.id, value: e.currentTarget.value })}
|
|
137
|
+
disabled={!selection.included}
|
|
138
|
+
placeholder="value"
|
|
139
|
+
/>
|
|
140
|
+
{/if}
|
|
141
|
+
</td>
|
|
142
|
+
</tr>
|
|
143
|
+
{/if}
|
|
144
|
+
{/snippet}
|
|
145
|
+
{#snippet empty()}
|
|
146
|
+
<EmptyState message="No features registered" />
|
|
147
|
+
{/snippet}
|
|
148
|
+
</DataTable>
|
|
149
|
+
</Card>
|
|
150
|
+
|
|
151
|
+
<Card>
|
|
152
|
+
{#snippet header()}
|
|
153
|
+
<Text size="caption" weight="semibold" color="subdued">GENERATED URL</Text>
|
|
154
|
+
{/snippet}
|
|
155
|
+
<Stack direction="vertical" gap="small">
|
|
156
|
+
<CodeBlock code={generatedUrl} />
|
|
157
|
+
<Stack direction="horizontal" gap="small" justify="center">
|
|
158
|
+
<CopyButton text={generatedUrl} label="Copy URL" />
|
|
159
|
+
<Button
|
|
160
|
+
variant="secondary"
|
|
161
|
+
size="small"
|
|
162
|
+
onclick={refreshPage}
|
|
163
|
+
disabled={!hasSelections}
|
|
164
|
+
>
|
|
165
|
+
Reload
|
|
166
|
+
</Button>
|
|
167
|
+
</Stack>
|
|
168
|
+
{#if !hasSelections}
|
|
169
|
+
<Text size="caption" color="subdued">
|
|
170
|
+
Select at least one feature above to add overrides to the URL.
|
|
171
|
+
</Text>
|
|
172
|
+
{/if}
|
|
173
|
+
</Stack>
|
|
174
|
+
</Card>
|
|
175
|
+
</Stack>
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getPicked } from '../../../../hooks'
|
|
3
|
+
import { Card, DataTable, EmptyState, Stack } from '../../ui'
|
|
3
4
|
|
|
4
5
|
const client = getPicked()
|
|
5
6
|
|
|
6
7
|
$: frameworks = client.frameworks.listStore
|
|
8
|
+
|
|
9
|
+
const columns = [{ key: 'name', header: 'Name' }]
|
|
7
10
|
</script>
|
|
8
11
|
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</tr>
|
|
15
|
-
</thead>
|
|
16
|
-
<tbody>
|
|
17
|
-
{#each $frameworks as framework (framework.name)}
|
|
12
|
+
<Stack padding="medium">
|
|
13
|
+
<Card sectioned>
|
|
14
|
+
<DataTable columns={columns} rows={$frameworks}>
|
|
15
|
+
{#snippet row(item)}
|
|
16
|
+
{@const framework = item}
|
|
18
17
|
<tr>
|
|
19
18
|
<td>{framework.name}</td>
|
|
20
19
|
</tr>
|
|
21
|
-
{/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
{/snippet}
|
|
21
|
+
{#snippet empty()}
|
|
22
|
+
<EmptyState message="No frameworks" />
|
|
23
|
+
{/snippet}
|
|
24
|
+
</DataTable>
|
|
25
|
+
</Card>
|
|
26
|
+
</Stack>
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HistoryEntry } from '@picked/client'
|
|
3
3
|
import HistoryEntryListItem from './HistoryEntryListItem.svelte'
|
|
4
|
+
import { Card, DataTable, EmptyState, Stack } from '../../ui'
|
|
4
5
|
|
|
5
6
|
export let historyEntries: HistoryEntry[]
|
|
7
|
+
|
|
8
|
+
const columns = [
|
|
9
|
+
{ key: 'id', header: 'ID' },
|
|
10
|
+
{ key: 'experienceId', header: 'Experience ID' },
|
|
11
|
+
{ key: 'nodeId', header: 'Node ID' },
|
|
12
|
+
{ key: 'nodeType', header: 'Node Type' },
|
|
13
|
+
{ key: 'at', header: 'At' },
|
|
14
|
+
]
|
|
6
15
|
</script>
|
|
7
16
|
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{/each}
|
|
22
|
-
</tbody>
|
|
23
|
-
</table>
|
|
17
|
+
<Stack padding="medium">
|
|
18
|
+
<Card sectioned>
|
|
19
|
+
<DataTable columns={columns} rows={historyEntries}>
|
|
20
|
+
{#snippet row(item)}
|
|
21
|
+
{@const historyEntry = item}
|
|
22
|
+
<HistoryEntryListItem {historyEntry} />
|
|
23
|
+
{/snippet}
|
|
24
|
+
{#snippet empty()}
|
|
25
|
+
<EmptyState message="No history entries" />
|
|
26
|
+
{/snippet}
|
|
27
|
+
</DataTable>
|
|
28
|
+
</Card>
|
|
29
|
+
</Stack>
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getPicked } from '../../../../hooks'
|
|
3
|
+
import { Card, DataTable, EmptyState, Stack } from '../../ui'
|
|
3
4
|
|
|
4
5
|
const client = getPicked()
|
|
5
6
|
|
|
6
7
|
$: predicates = client.predicates.listStore
|
|
8
|
+
|
|
9
|
+
const columns = [{ key: 'name', header: 'Name' }]
|
|
7
10
|
</script>
|
|
8
11
|
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</tr>
|
|
15
|
-
</thead>
|
|
16
|
-
<tbody>
|
|
17
|
-
{#each $predicates as predicate (predicate.name)}
|
|
12
|
+
<Stack padding="medium">
|
|
13
|
+
<Card sectioned>
|
|
14
|
+
<DataTable columns={columns} rows={$predicates}>
|
|
15
|
+
{#snippet row(item)}
|
|
16
|
+
{@const predicate = item}
|
|
18
17
|
<tr>
|
|
19
18
|
<td>{predicate.name}</td>
|
|
20
19
|
</tr>
|
|
21
|
-
{/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
{/snippet}
|
|
21
|
+
{#snippet empty()}
|
|
22
|
+
<EmptyState message="No predicates" />
|
|
23
|
+
{/snippet}
|
|
24
|
+
</DataTable>
|
|
25
|
+
</Card>
|
|
26
|
+
</Stack>
|
|
@@ -1 +1,47 @@
|
|
|
1
|
-
<
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getPicked } from '../../../../hooks'
|
|
3
|
+
import { Button, Card, CodeBlock, DataTable, Stack, Text } from '../../ui'
|
|
4
|
+
|
|
5
|
+
const client = getPicked()
|
|
6
|
+
|
|
7
|
+
const dashboardProjectUrl = `${client.dashboardUrl}/p/${client.projectId}`
|
|
8
|
+
|
|
9
|
+
const columns = [
|
|
10
|
+
{ key: 'label', header: 'Property' },
|
|
11
|
+
{ key: 'value', header: 'Value' },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const rows = [
|
|
15
|
+
{ label: 'Project ID', value: client.projectId },
|
|
16
|
+
{ label: 'Organization ID', value: client.organizationId },
|
|
17
|
+
{ label: 'API URL', value: client.apiUrl },
|
|
18
|
+
{ label: 'Dashboard URL', value: client.dashboardUrl },
|
|
19
|
+
]
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<Stack direction="vertical" gap="medium" padding="medium">
|
|
23
|
+
<Text size="caption" color="subdued">
|
|
24
|
+
Project configuration for this Picked client instance.
|
|
25
|
+
</Text>
|
|
26
|
+
|
|
27
|
+
<Card sectioned>
|
|
28
|
+
<DataTable {columns} {rows}>
|
|
29
|
+
{#snippet row(item)}
|
|
30
|
+
<tr>
|
|
31
|
+
<td>
|
|
32
|
+
<Text size="small" weight="medium">{item.label}</Text>
|
|
33
|
+
</td>
|
|
34
|
+
<td>
|
|
35
|
+
<CodeBlock code={item.value} inline />
|
|
36
|
+
</td>
|
|
37
|
+
</tr>
|
|
38
|
+
{/snippet}
|
|
39
|
+
</DataTable>
|
|
40
|
+
</Card>
|
|
41
|
+
|
|
42
|
+
<Stack direction="horizontal" gap="small">
|
|
43
|
+
<Button variant="primary" size="small" href={dashboardProjectUrl} target="_blank">
|
|
44
|
+
Open in Dashboard
|
|
45
|
+
</Button>
|
|
46
|
+
</Stack>
|
|
47
|
+
</Stack>
|