Skip to content

Commit

Permalink
fix(*): custom plugin support
Browse files Browse the repository at this point in the history
  • Loading branch information
kaiarrowood committed Oct 27, 2023
1 parent 8bc0c93 commit d4c547a
Show file tree
Hide file tree
Showing 8 changed files with 563 additions and 426 deletions.
10 changes: 10 additions & 0 deletions packages/entities/entities-plugins/sandbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ const init = async () => {
name: 'create-plugin',
component: () => import('./pages/FallbackPage.vue'),
},
{
path: '/custom-plugin/create',
name: 'create-custom-plugin',
component: () => import('./pages/FallbackPage.vue'),
},
{
path: '/plugin/:plugin/:id',
name: 'view-plugin',
component: () => import('./pages/PluginConfigCardPage.vue'),
props: true,
},
{
path: '/custom-plugin/:plugin/edit',
name: 'edit-custom-plugin',
component: () => import('./pages/FallbackPage.vue'),
},
{
path: '/plugin/:id/edit',
name: 'edit-plugin',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ const konnectConfig = ref<KonnectPluginFormConfig>({
// entity_type: 'consumer_id',
},
}),
createCustomRoute: { name: 'create-custom-plugin' },
getCustomEditRoute: (plugin: string) => ({
name: 'edit-custom-plugin',
params: {
control_plane_id: controlPlaneId.value,
plugin,
},
}),
})
const kongManagerConfig = ref<KongManagerPluginFormConfig>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ import { useErrors } from '@kong-ui-public/entities-shared'
const props = defineProps({
plugin: {
type: Object as PropType<{name: string, id: string}>,
type: Object as PropType<{ name: string, id: string }>,
required: true,
},
})
Expand Down
311 changes: 243 additions & 68 deletions packages/entities/entities-plugins/src/components/PluginCustomGrid.vue
Original file line number Diff line number Diff line change
@@ -1,99 +1,274 @@
<template>
<div>
<!-- <KCollapse
<div class="custom-plugins-grid">
<KEmptyState
v-if="!modifiedCustomPlugins.length"
class="custom-plugins-empty-state"
cta-is-hidden
icon="stateGruceo"
icon-size="96"
>
<!-- this will only be shown if not allowed to create custom plugins -->
<template #title>
<span class="empty-state-title">
{{ t('plugins.select.tabs.custom.empty_title') }}
</span>
</template>

<template #message>
<span class="empty-state-description">
{{ t('plugins.select.tabs.custom.empty_description') }}
</span>
</template>
</KEmptyState>

<KCollapse
v-else
v-model="shouldCollapsedCustomPlugins"
class="plugins-collapse"
:data-testid="`${PLUGIN_GROUPS.CUSTOM_PLUGINS}-collapse`"
:trigger-label="shouldCollapsedCustomPlugins ? triggerLabels[PLUGIN_GROUPS.CUSTOM_PLUGINS] : pluginHelpText.viewLess"
class="custom-plugins-collapse"
:data-testid="`${PluginGroup.CUSTOM_PLUGINS}-collapse`"
:title="PluginGroup.CUSTOM_PLUGINS"
:trigger-label="triggerLabel"
>
<!-- don't display a trigger if all plugins will already be visible -->
<template
v-if="filteredCustomPlugins.length <= pluginsPerRow"
v-if="modifiedCustomPlugins.length <= pluginsPerRow"
#trigger
>
&nbsp;
&nbsp;
</template>
<template #visible-content>
<div class="plugin-card-container">
<component
:is="componentType(plugin)"
v-for="(plugin, index) in getPluginCards(filteredCustomPlugins, 'visible')"
<PluginSelectCard
v-for="(plugin, index) in getPluginCards('visible')"
:key="index"
class="plugin-card"
:class="{
'disabled': !plugin.available || plugin.disabledMessage,
'plugin-card-cursor-pointer': isCreateCustomPlugin(plugin) || componentType(plugin) === 'div' && noRouteChange
}"
:label="plugin.disabledMessage"
:position-fixed="plugin.disabledMessage ? true : undefined"
:title="!plugin.available ? 'This plugin is not available' : plugin.name"
:to="componentType(plugin) === 'router-link' ? getOnClickRoute(plugin) : undefined"
>
<template #content>
<div style="max-width: 345px;">
{{ plugin.disabledMessage }}
</div>
</template>
<PluginCard
:no-route-change="noRouteChange"
:plugin="plugin"
:runtime-group-id="runtimeGroupId"
@click="props.noRouteChange ? emitPluginData(plugin) : undefined"
@custom-plugin-clicked="handleCustomPluginClick(plugin)"
@custom-plugin-delete="handleCustomPluginDelete(plugin)"
/>
</component>
:can-delete-custom="canDeleteCustom"
:can-edit-custom="canEditCustom"
:config="config"
:no-route-change="noRouteChange"
:plugin="plugin"
@custom-plugin-delete="() => handleCustomPluginDelete(plugin)"
@plugin-clicked="emitPluginData"
/>
</div>
</template>

<div class="plugin-card-container">
<component
:is="componentType(plugin)"
v-for="(plugin, index) in getPluginCards(filteredCustomPlugins, 'hidden')"
<PluginSelectCard
v-for="(plugin, index) in getPluginCards('hidden')"
:key="index"
class="plugin-card"
:class="{
'disabled': !plugin.available || plugin.disabledMessage,
'plugin-card-cursor-pointer': isCreateCustomPlugin(plugin) || componentType(plugin) === 'div' && noRouteChange
}"
:label="plugin.disabledMessage"
:position-fixed="plugin.disabledMessage ? true : undefined"
:title="!plugin.available ? 'This plugin is not available' : plugin.name"
:to="componentType(plugin) === 'router-link' ? getOnClickRoute(plugin) : undefined"
>
<template #content>
<div style="max-width: 345px;">
{{ plugin.disabledMessage }}
</div>
</template>
<PluginCard
:plugin="plugin"
:runtime-group-id="runtimeGroupId"
@click="props.noRouteChange ? emitPluginData(plugin) : undefined"
@custom-plugin-clicked="handleCustomPluginClick(plugin)"
@custom-plugin-delete="handleCustomPluginDelete(plugin)"
/>
</component>
:can-delete-custom="canDeleteCustom"
:can-edit-custom="canEditCustom"
:config="config"
:no-route-change="noRouteChange"
:plugin="plugin"
@plugin-clicked="emitPluginData"
/>
</div>
</KCollapse>

<DeleteCustomPluginSchemaModal
v-if="openDeleteModal"
v-if="openDeleteModal && selectedPlugin"
:plugin="selectedPlugin"
@closed="handleClose"
@proceed="handleCustomPluginDeleted"
/> -->
@proceed="() => handleClose(true)"
/>
</div>
</template>

<script setup lang="ts">
/* import { ref } from 'vue'
import { computed, ref, type PropType } from 'vue'
import {
PluginGroup,
type KongManagerPluginFormConfig,
type KonnectPluginFormConfig,
type PluginType,
type PluginCardList,
} from '../types'
import composables from '../composables'
import PluginSelectCard from './PluginSelectCard.vue'
import DeleteCustomPluginSchemaModal from './DeleteCustomPluginSchemaModal.vue'
const shouldCollapsedCustomPlugins = ref(true) */
const props = defineProps({
/** The base konnect or kongManger config. Pass additional config props in the shared entity component as needed. */
config: {
type: Object as PropType<KonnectPluginFormConfig | KongManagerPluginFormConfig>,
required: true,
validator: (config: KonnectPluginFormConfig | KongManagerPluginFormConfig): boolean => {
if (!config || !['konnect', 'kongManager'].includes(config?.app)) return false
if (!config.getCreateRoute) return false
return true
},
},
/**
* Whether or not user has rights to create custom plugins
*/
canCreate: {
type: Boolean,
default: false,
},
/**
* Whether or not user has rights to delete custom plugins
*/
canDeleteCustom: {
type: Boolean,
default: false,
},
/**
* Whether or not user has rights to edit custom plugins
*/
canEditCustom: {
type: Boolean,
default: false,
},
/**
* Plugins to display in the grid
*/
pluginList: {
type: Object as PropType<PluginCardList>,
default: () => ({}),
},
/**
* @param {boolean} noRouteChange if true, let consuming component handle event when clicking on a plugin
* Used in conjunction with `@plugin-clicked` event
*/
noRouteChange: {
type: Boolean,
default: false,
},
/**
* Number of plugins to always have visible (never will be collapsed)
*/
pluginsPerRow: {
type: Number,
default: 4,
},
})
const emit = defineEmits<{
(e: 'plugin-clicked', plugin: PluginType): void,
(e: 'revalidate'): void,
}>()
const { i18n: { t } } = composables.useI18n()
const shouldCollapsedCustomPlugins = ref(true)
const emitPluginData = (plugin: PluginType) => {
emit('plugin-clicked', plugin)
}
const modifiedCustomPlugins = computed((): PluginType[] => {
if (props.config.app === 'kongManager') {
return []
}
const customPlugins: PluginType[] = JSON.parse(JSON.stringify(props.pluginList))[PluginGroup.CUSTOM_PLUGINS] || []
// ADD CUSTOM_PLUGIN_CREATE as the first card if allowed creation
return props.canCreate && !props.noRouteChange && props.config.createCustomRoute
? [{
id: 'custom-plugin-create',
name: t('plugins.select.tabs.custom.create.name'),
available: true,
group: PluginGroup.CUSTOM_PLUGINS,
description: t('plugins.select.tabs.custom.create.description'),
} as PluginType].concat(customPlugins)
: customPlugins
})
const getPluginCards = (type: 'all' | 'visible' | 'hidden') => {
if (type === 'all') {
return modifiedCustomPlugins.value
} else if (type === 'visible') {
return modifiedCustomPlugins.value.slice(0, props.pluginsPerRow)
}
return modifiedCustomPlugins.value.slice(props.pluginsPerRow)
}
// text for plugin group "view x more" label
const triggerLabel = computed((): string => {
const totalCount = getPluginCards('all')?.length || 0
const hiddenCount = getPluginCards('hidden')?.length || 0
if (totalCount > props.pluginsPerRow) {
return t('plugins.select.view_more', { count: hiddenCount })
}
return t('plugins.select.view_less')
})
const openDeleteModal = ref(false)
const selectedPlugin = ref<{ name: string, id: string } | null>(null)
const handleCustomPluginDelete = (plugin: PluginType): void => {
openDeleteModal.value = true
selectedPlugin.value = {
id: plugin.id,
name: plugin.name,
}
}
const handleClose = (revalidate?: boolean): void => {
if (revalidate) {
emit('revalidate')
}
openDeleteModal.value = false
selectedPlugin.value = null
}
</script>

<style lang="scss" scoped>
/**
// TODO:
*/
.custom-plugins-grid {
:deep(.empty-state-wrapper) {
.custom-plugins-empty-state {
padding-bottom: $kui-space-0;
.empty-state-title {
font-size: $kui-font-size-40;
font-weight: $kui-font-weight-semibold;
}
.empty-state-description {
font-size: $kui-font-size-30;
margin-left: $kui-space-60;
margin-right: $kui-space-60;
}
}
}
.plugins-collapse {
margin-bottom: $kui-space-90;
}
.plugin-card-container {
column-gap: $kui-space-80;
display: grid;
grid-auto-rows: 1fr;
margin-top: $kui-space-90;
row-gap: $kui-space-90;
// TODO: do I still need this?
:deep(.kong-card) {
display: flex;
flex: 1 0 0;
flex-direction: column;
margin: $kui-space-0;
padding: $kui-space-0;
text-align: center;
}
@media (min-width: $kui-breakpoint-phablet) {
grid-template-columns: repeat(2, 1fr);
}
@media (min-width: $kui-breakpoint-tablet) {
grid-template-columns: repeat(3, 1fr);
}
@media (min-width: $kui-breakpoint-laptop) {
grid-template-columns: repeat(4, 1fr);
}
}
}
</style>
Loading

0 comments on commit d4c547a

Please sign in to comment.