Skip to content

Commit

Permalink
Merge pull request #34 from krumIO/feat/APPLAUNCHER-28__global_card_c…
Browse files Browse the repository at this point in the history
…luster_name

feat: for APPLAUNCHER-28 various data fixes
  • Loading branch information
Sinjhin authored Apr 15, 2024
2 parents 9e28462 + 4882b61 commit 145565f
Show file tree
Hide file tree
Showing 6 changed files with 458 additions and 325 deletions.
69 changes: 33 additions & 36 deletions pkg/app-launcher/components/AppLauncherCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,79 +10,72 @@ export default {
ButtonDropDown,
},
props: {
clusterId: {
type: String,
required: true,
},
favoritedServices: {
favoritedApps: {
type: Array,
required: true,
},
service: {
app: {
type: Object,
default: null,
},
ingress: {
type: Object,
default: null,
isInGlobalView: {
type: Boolean,
default: false,
},
},
methods: {
openLink(link) {
window.open(link);
},
toggleFavorite() {
this.$emit('toggle-favorite', this.service);
this.$emit('toggle-favorite', this.app)
},
},
computed: {
app() {
return this.service || this.ingress;
},
endpoints() {
return (
this.service?.spec?.ports?.map((port) => {
this.app?.spec?.ports?.map((port) => {
const endpoint = `${
isMaybeSecure(port.port, port.protocol) ? 'https' : 'http'
}:${this.service?.metadata?.name}:${port.port}`;
}:${this.app.metadata?.name}:${port.port}`;
return {
label: `${endpoint}${port.protocol === 'UDP' ? ' (UDP)' : ''}`,
value: `/k8s/clusters/${this.clusterId}/api/v1/namespaces/${this.service.metadata.namespace}/services/${endpoint}/proxy`,
value: `/k8s/clusters/${this.clusterId}/api/v1/namespaces/${this.app.namespace}/services/${endpoint}/proxy`,
};
}) ?? []
);
},
computedServiceName() {
return (
this.service?.metadata?.labels?.['app.kubernetes.io/component'] ??
this.service?.metadata?.name
this.app?.metadata?.labels?.['app.kubernetes.io/component'] ??
this.app?.metadata?.name
);
},
helmChart() {
return this.service?.metadata?.labels?.['helm.sh/chart'];
return this.app?.metadata?.labels?.['helm.sh/chart'];
},
kubernetesVersion() {
return this.service?.metadata?.labels?.['app.kubernetes.io/version'];
return this.app?.metadata?.labels?.['app.kubernetes.io/version'];
},
name() {
return this.service?.metadata?.name;
return this.app?.metadata?.name;
},
namespace() {
return this.service?.metadata?.namespace;
return this.app?.metadata?.namespace;
},
isFavorited() {
return this.favoritedServices.some(
(favoritedService) =>
favoritedService?.clusterId === this.clusterId &&
favoritedService?.service?.id === this.service?.id
return this.favoritedApps.some(
(favoritedApp) =>
favoritedApp?.id === this.app.id &&
favoritedApp?.kind === this.app.kind
);
},
isGlobalApp() {
return this.service?.metadata?.annotations?.['extensions.applauncher/global-app'] === 'true';
return this.app?.metadata?.annotations?.['extensions.applauncher/global-app'] === 'true';
},
ingressPath() {
return ingressFullPath(this.ingress, this.ingress?.spec?.rules?.[0]);
return ingressFullPath(this.app, this.app?.spec?.rules?.[0]);
}
},
name: 'AppLauncherCard',
Expand All @@ -95,32 +88,36 @@ export default {
<template #title>
<div style="width: 100%">
<p style="font-size: 1.2rem">
{{ service ? service?.metadata?.name : ingress?.metadata?.name }}
{{ app.metadata?.name }}
</p>
<div style="color: var(--input-label); display: flex; justify-content: space-between; margin-top: 4px;">
<p v-if="app.type === 'service' && app.metadata?.labels?.['app.kubernetes.io/version'] !== undefined">
<p v-if="app.kind === 'Service' && app.metadata?.labels?.['app.kubernetes.io/version'] !== undefined">
{{ kubernetesVersion }}
</p>
<p v-if="app.type === 'service' && app.metadata?.labels?.['helm.sh/chart'] !== undefined">
<p v-if="app.kind === 'Service' && app.metadata?.labels?.['helm.sh/chart'] !== undefined">
{{ helmChart }}
</p>
<p v-if="app.type === 'ingress'">
<p v-if="app.kind === 'Ingress'">
Ingress
</p>
</div>
</div>
</template>
<template #body>
<p v-if="app.type === 'service'">{{ namespace }}/{{ name }}</p>
<p v-if="app.type === 'ingress'">{{ ingressPath }}</p>
<p v-if="app.kind === 'Service'">
{{ (isGlobalApp || isFavorited) && isInGlobalView ? `${app.clusterName}/` : '' }}{{ namespace }}/{{ name }}
</p>
<p v-if="app.kind === 'Ingress'">
{{ (isGlobalApp || isFavorited) && isInGlobalView ? `${app.clusterName}: ` : '' }}{{ ingressPath }}
</p>
</template>
<template #actions>
<button v-if="!isGlobalApp" class="icon-button" @click="toggleFavorite">
<i :class="['icon', isFavorited ? 'icon-star' : 'icon-star-open']" />
</button>
<i v-else class="icon icon-globe icon-only" />
<a
v-if="(endpoints?.length ?? 0) <= 1 && app.type === 'service'"
v-if="(endpoints?.length ?? 0) <= 1 && app.kind === 'Service'"
:disabled="!endpoints?.length"
:href="endpoints[0]?.value"
target="_blank"
Expand All @@ -133,7 +130,7 @@ export default {
{{ t('appLauncher.launch') }}
</a>
<a
v-else-if="app.type === 'ingress'"
v-else-if="app.kind === 'Ingress'"
:href="ingressPath"
target="_blank"
rel="noopener noreferrer nofollow"
Expand Down
143 changes: 143 additions & 0 deletions pkg/app-launcher/components/ClusterActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<script>
export default {
name: 'ClusterActions',
props: {
searchQuery: {
type: String,
required: true,
},
isGridView: {
type: Boolean,
required: true,
},
sortOrder: {
type: String,
required: true,
},
selectedCluster: {
type: String,
required: true,
},
clusterOptions: {
type: Array,
required: true,
},
},
computed: {
aToZorZtoA() {
console.log(this.$store.getters['i18n/t']('appLauncher.aToZ'));
return this.sortOrder === 'asc' ? this.$store.getters['i18n/t']('appLauncher.aToZ') : this.$store.getters['i18n/t']('appLauncher.zToA');
},
},
emits: ['update:search-query', 'toggle-sort', 'update:selected-cluster', 'set-view'],
};
</script>

<template>
<div class="cluster-actions">
<div class="search-input">
<input :value="searchQuery" :placeholder="$store.getters['i18n/t']('appLauncher.filter')" @input="$emit('update:search-query', $event.target.value)" />
</div>
<div class="sort-buttons" v-if="isGridView" @click="$emit('toggle-sort')">
<div class="sort-button" :class="{ active: sortOrder === 'asc' }" :disabled="sortOrder === 'asc'">
<i class="icon-chevron-up"></i>
</div>
<div class="sort-label">
<p>{{ aToZorZtoA }}</p>
</div>
<div class="sort-button" :class="{ active: sortOrder === 'desc' }" :disabled="sortOrder === 'desc'">
<i class="icon-chevron-down"></i>
</div>
</div>
<div class="select-wrapper">
<select :value="selectedCluster" class="cluster-select" @change="$emit('update:selected-cluster', $event.target.value)">
<option v-for="option in clusterOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
</div>
<button class="icon-button" @click="$emit('set-view', 'grid')">
<i class="icon icon-apps" />
</button>
<button class="icon-button" @click="$emit('set-view', 'list')">
<i class="icon icon-list-flat" />
</button>
</div>
</template>


<style scoped>
.cluster-actions {
display: flex;
align-items: center;
gap: 1rem;
position: fixed;
right: 5.8%;
top: 4.3rem;
z-index: 2;
padding-bottom: 0.425rem;
padding-right: 4.4rem;
background: inherit;
border-bottom: var(--header-border-size) solid var(--header-border);
}
.icon-button {
background: none;
border: none;
cursor: pointer;
padding: 0;
color: var(--primary);
font-size: 1.8rem;
}
.sort-button {
background: none;
border: none;
cursor: pointer;
padding: none;
color: #555555;
font-size: 1.3rem;
display: flex;
align-items: center;
}
.sort-label {
color: var(--primary);
font-size: 1rem;
cursor: pointer;
}
.sort-buttons {
display: flex;
gap: 1rem;
align-items: center;
}
.sort-button:hover {
color: var(--primary-hover);
}
.sort-button.active {
color: var(--primary);
}
.search-input {
text-align: right;
justify-content: flex-end;
display: flex;
input {
width: 190px;
padding: 11px;
font-size: 1rem;
border: 1px solid var(--border);
border-radius: 4px;
}
}
.select-wrapper select {
padding: 0.8rem;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
64 changes: 64 additions & 0 deletions pkg/app-launcher/components/ClusterGridView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script>
import AppLauncherCard from './AppLauncherCard.vue';
export default {
name: 'ClusterGridView',
components: {
AppLauncherCard,
},
props: {
clusterData: {
type: Object,
required: true,
},
favoritedApps: {
type: Array,
required: true,
},
},
emits: ['toggle-favorite'],
};
</script>

<template>
<div class="cluster-grid-view">
<div class="cluster-header">
<h1>
{{ clusterData.name }}
</h1>
</div>
<div class="services-by-cluster-grid">
<AppLauncherCard
v-for="app in clusterData.filteredApps"
:key="`${app.clusterId}-${app.id}-${app.kind}`"
:app="app" :isInGlobalView="false" :favorited-apps="favoritedApps"
@toggle-favorite="$emit('toggle-favorite', $event)"
/>
</div>
</div>
</template>

<style scoped lang="scss">
.services-by-cluster-grid {
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.cluster-header {
display: flex;
align-items: center;
justify-content: space-between;
margin: 1rem 0;
background: var(--header-bg);
border-bottom: var(--header-border-size) solid var(--header-border);
height: var(--header-height);
position: sticky;
top: 0;
z-index: 1;
}
.favorite-icon {
margin-right: 1rem;
}
</style>
Loading

0 comments on commit 145565f

Please sign in to comment.