Skip to content

Commit 7a8d46a

Browse files
LZylstrabryantgillespieCopilot
authored
Add docs and resources section to integration pages (#212)
* Add docs and resources section to integration pages * Update layers/marketplace/pages/integrations/[integration].vue Co-authored-by: Copilot <[email protected]> * add status * copilot stinks --------- Co-authored-by: Bryant Gillespie <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: bryantgillespie <[email protected]>
1 parent 02e46a0 commit 7a8d46a

File tree

4 files changed

+210
-4
lines changed

4 files changed

+210
-4
lines changed

layers/marketplace/pages/integrations/[integration].vue

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,38 @@ useSchemaOrg([
7979
/>
8080
</div>
8181
</section>
82+
<!-- External Resources Section -->
83+
<section v-if="integration?.external_resources?.length" id="external-resources">
84+
<BaseHeading tag="h2" content="Documentation & Resources" size="medium" />
85+
<div class="external-resources-list">
86+
<a
87+
v-for="resource in integration.external_resources"
88+
:key="resource.id"
89+
:href="resource.url"
90+
target="_blank"
91+
rel="noopener noreferrer"
92+
class="external-resource-card"
93+
>
94+
<div v-if="resource.image" class="resource-image">
95+
<BaseDirectusImage
96+
:width="120"
97+
:height="120"
98+
:uuid="typeof resource.image === 'string' ? resource.image : resource.image?.id"
99+
:alt="resource.title"
100+
/>
101+
</div>
102+
<div v-else class="resource-image-placeholder">
103+
<BaseIcon name="description" size="large" />
104+
</div>
105+
<div class="resource-content">
106+
<h3 class="resource-title">{{ resource.title }}</h3>
107+
<p v-if="resource.description" class="resource-description">{{ resource.description }}</p>
108+
</div>
109+
<BaseIcon name="open_in_new" class="external-link-icon" size="small" />
110+
</a>
111+
</div>
112+
</section>
113+
<!-- Overview Section -->
82114
<section id="overview">
83115
<BaseHeading tag="h2" content="Overview" size="medium" />
84116
<BaseText v-if="integration?.content" :content="integration?.content" color="foreground" class="mt-4" />
@@ -317,6 +349,124 @@ useSchemaOrg([
317349
margin-block-start: var(--space-6);
318350
}
319351
352+
.external-resources-list {
353+
display: flex;
354+
flex-direction: column;
355+
gap: var(--space-4);
356+
margin-block-start: var(--space-6);
357+
}
358+
359+
.external-resource-card {
360+
display: flex;
361+
align-items: center;
362+
gap: var(--space-4);
363+
padding: var(--space-4);
364+
border-radius: var(--rounded-lg);
365+
border: 1px solid var(--gray-200);
366+
background-color: var(--background);
367+
color: var(--foreground);
368+
text-decoration: none;
369+
transition: all var(--duration-200) var(--ease-out);
370+
position: relative;
371+
372+
&:hover {
373+
border-color: var(--gray-300);
374+
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
375+
transform: translateY(-2px);
376+
377+
.resource-title {
378+
color: var(--primary);
379+
}
380+
381+
.external-link-icon {
382+
color: var(--primary);
383+
}
384+
}
385+
386+
&:focus {
387+
outline: none;
388+
border-color: var(--primary);
389+
box-shadow: 0 0 0 3px var(--primary-100);
390+
}
391+
392+
&:focus-visible {
393+
outline: none;
394+
border-color: var(--primary);
395+
box-shadow: 0 0 0 3px var(--primary-100);
396+
}
397+
}
398+
399+
.resource-image {
400+
flex-shrink: 0;
401+
width: 120px;
402+
height: 120px;
403+
border-radius: var(--rounded-md);
404+
overflow: hidden;
405+
background-color: var(--gray-100);
406+
display: flex;
407+
align-items: center;
408+
justify-content: center;
409+
410+
@media (width <= 30rem) {
411+
display: none;
412+
}
413+
414+
img {
415+
width: 100%;
416+
height: 100%;
417+
object-fit: cover;
418+
}
419+
}
420+
421+
.resource-image-placeholder {
422+
flex-shrink: 0;
423+
width: 120px;
424+
height: 120px;
425+
border-radius: var(--rounded-md);
426+
background-color: var(--gray-100);
427+
display: flex;
428+
align-items: center;
429+
justify-content: center;
430+
color: var(--gray-400);
431+
432+
@media (width <= 30rem) {
433+
display: none;
434+
}
435+
}
436+
437+
.resource-content {
438+
flex: 1;
439+
display: flex;
440+
flex-direction: column;
441+
gap: var(--space-2);
442+
min-width: 0;
443+
}
444+
445+
.resource-title {
446+
font-size: var(--font-size-lg);
447+
font-weight: 600;
448+
line-height: var(--line-height-lg);
449+
margin: 0;
450+
color: var(--foreground);
451+
transition: color var(--duration-200) var(--ease-out);
452+
}
453+
454+
.resource-description {
455+
color: var(--gray-600);
456+
font-size: var(--font-size-sm);
457+
line-height: var(--line-height-sm);
458+
margin: 0;
459+
}
460+
461+
.external-link-icon {
462+
position: absolute;
463+
top: var(--space-4);
464+
right: var(--space-4);
465+
flex-shrink: 0;
466+
color: var(--gray-400);
467+
transition: color var(--duration-200) var(--ease-out);
468+
}
469+
320470
.desktop-only {
321471
display: none;
322472

layers/marketplace/server/api/integrations/[integration].get.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import type { MarketplaceIntegration, MarketplaceExtension } from '~/types/marketplace';
2+
import type { IntegrationExternalResource } from '~/types/schema/marketplace/integration-external-resource';
23
import { arrayToString } from '~/utils/arrayToString';
4+
import { isUuid } from '~/layers/marketplace/server/utils/isUuid';
35
import { typesenseServer } from '~/layers/marketplace/server/services/typesense';
46
import { consola } from 'consola';
57

68
interface IntegrationWithExtensions extends MarketplaceIntegration {
79
extensionDetails?: MarketplaceExtension[];
10+
external_resources?: IntegrationExternalResource[];
811
}
912

1013
const config = useRuntimeConfig();
@@ -27,14 +30,38 @@ export default defineEventHandler(
2730
const response = await $fetch<{ data?: MarketplaceIntegration[] } | MarketplaceIntegration[]>(
2831
`${directusUrl}/items/integrations`,
2932
{
30-
params: {
31-
fields: ['*'],
32-
filter,
33+
query: {
34+
fields: [
35+
'*',
36+
'external_resources.id',
37+
'external_resources.title',
38+
'external_resources.url',
39+
'external_resources.description',
40+
'external_resources.image',
41+
'external_resources.sort',
42+
'external_resources.status',
43+
],
44+
filter: {
45+
...filter,
46+
status: {
47+
_eq: 'published',
48+
},
49+
},
50+
deep: {
51+
external_resources: {
52+
_filter: {
53+
status: {
54+
_eq: 'published',
55+
},
56+
},
57+
_sort: ['sort'],
58+
},
59+
},
3360
},
3461
},
3562
);
3663

37-
const integration = Array.isArray(response) ? response[0] : (response as any)?.data[0] || [];
64+
const integration = Array.isArray(response) ? response[0] : (response as any)?.data?.[0] || null;
3865

3966
if (!integration) {
4067
throw createError({
@@ -66,9 +93,15 @@ export default defineEventHandler(
6693
}
6794
}
6895

96+
const externalResources: IntegrationExternalResource[] =
97+
integration.external_resources && Array.isArray(integration.external_resources)
98+
? integration.external_resources
99+
: [];
100+
69101
return {
70102
...integration,
71103
extensionDetails,
104+
external_resources: externalResources,
72105
};
73106
} catch (error) {
74107
consola.error('Integration detail API error:', error);

types/marketplace.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ export interface MarketplaceIntegration {
155155
}>;
156156
content: string;
157157
extensionDetails?: MarketplaceExtension[];
158+
external_resources?: Array<{
159+
id: string;
160+
title: string;
161+
url: string;
162+
description?: string | null;
163+
image?: File | string | null;
164+
sort?: number | null;
165+
status?: string | null;
166+
}>;
158167
}
159168

160169
export interface MarketplaceRequest {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { File } from '../system';
2+
3+
export interface IntegrationExternalResource {
4+
/** @required */
5+
id: string;
6+
sort?: number | null;
7+
/** @required */
8+
title: string;
9+
/** @required */
10+
url: string;
11+
description?: string | null;
12+
image?: File | string | null;
13+
integration?: string | null;
14+
}

0 commit comments

Comments
 (0)