Skip to content

Commit

Permalink
Rewrite GroupSelect in TS & add features
Browse files Browse the repository at this point in the history
  • Loading branch information
ingalls committed Dec 6, 2024
1 parent 88186de commit 8a57744
Show file tree
Hide file tree
Showing 4 changed files with 1,773 additions and 521 deletions.
21 changes: 21 additions & 0 deletions api/web/src/components/CloudTAK/Menu/Videos/VideoLeaseModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
<div class='col-12'>
<TablerInput
v-model='editLease.name'
description='The human readable name of the Lease'
label='Lease Name'
/>
</div>
Expand All @@ -125,9 +126,20 @@
<div class='col-12'>
<TablerToggle
v-model='shared'
description='By Default only the user that created a Lease can manage it. If you are operating as part of an agency, turn on Lease sharing to allow all users in your Channel to manage the lease'
label='Shared Lease'
/>
</div>
<div
v-if='shared'
class='col-12'
>
<GroupSelect
v-model='channels'
:limit='1'
/>
</div>

<div class='col-12'>
<label
class='subheader mt-3 cursor-pointer'
Expand Down Expand Up @@ -234,6 +246,7 @@ import { std } from '../../../../std.ts';
import CopyField from '../../util/CopyField.vue';
import { ref, onMounted } from 'vue';
import type { VideoLease, VideoLeaseResponse, VideoLeaseProtocols } from '../../../../types.ts';
import GroupSelect from '../../../util/GroupSelect.vue';
import { useProfileStore } from '../../../../stores/profile.ts';
import {
IconRefresh,
Expand Down Expand Up @@ -265,6 +278,8 @@ const wizard = ref(0);
const advanced = ref(false);
const protocols = ref<VideoLeaseProtocols>({});
const channels = ref<string[]>([]);
const profileStore = useProfileStore();
const durations = ref<Array<string>>(["16 Hours", "12 Hours", "6 Hours", "1 Hour"]);
Expand Down Expand Up @@ -318,6 +333,12 @@ async function fetchLease() {
duration: '16 Hours'
}
if (editLease.value.channel) {
channels.value = [ editLease.value.channel ];
} else {
channels.value = [];
}
if (res.lease.channel) {
shared.value = true;
} else {
Expand Down
185 changes: 92 additions & 93 deletions api/web/src/components/util/GroupSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@
placeholder='Filter Channels...'
/>

<div
v-if='props.limit'
class='alert alert-info mt-2'
role='alert'
>
<div class='d-flex align-items-center'>
<IconInfoCircle /><span class='mx-1'>Select up to&nbsp;<span v-text='props.limit' />&nbsp;Channel<span v-text='props.limit > 1 ? "s" : ""' /></span>
</div>
</div>

<TablerLoading
v-if='loading.groups'
v-if='loading'
desc='Loading Channels'
/>
<TablerNone
Expand All @@ -16,7 +26,10 @@
:create='false'
/>
<template v-else>
<div class='my-2'>
<div
class='my-2 overflow-auto'
style='height: 25vh;'
>
<div
v-for='group in filtered'
:key='group'
Expand All @@ -26,13 +39,13 @@
<IconCircleFilled
v-if='selected.has(group)'
:size='32'
:stroke='1'
stroke='1'
class='cursor-pointer'
/>
<IconCircle
v-else
:size='32'
:stroke='1'
troke='1'
class='cursor-pointer'
/>
<span
Expand All @@ -45,106 +58,92 @@
</div>
</template>

<script>
import { std, stdurl } from '/src/std.ts';
<script setup lang='ts'>
import { ref, computed, onMounted } from 'vue';
import { std, stdurl } from '../../../src/std.ts';
import { useProfileStore } from '../../../src/stores/profile.ts';
import type { Group } from '../../../src/types.ts';
import {
TablerLoading,
TablerInput,
TablerNone,
} from '@tak-ps/vue-tabler';
import {
IconCircle,
IconInfoCircle,
IconCircleFilled
} from '@tabler/icons-vue';
export default {
name: 'GroupSelectModal',
components: {
IconCircle,
IconCircleFilled,
TablerNone,
TablerInput,
TablerLoading
},
props: {
disabled: {
type: Boolean
},
button: {
type: Boolean,
default: false
},
connection: {
type: Number
},
modelValue: {
type: Array,
default: function() {
return []
}
}
},
emits: [
'update:modelValue'
],
data: function() {
return {
filter: '',
loading: {
groups: true,
},
selected: new Set(this.modelValue),
groups: []
}
},
computed: {
filtered: function() {
return Object.keys(this.groups).filter((g) => {
return g.toLowerCase().includes(this.filter.toLowerCase());
});
const props = defineProps<{
connection?: number,
limit?: number,
modelValue: Array<string>
}>();
const emit = defineEmits([
'update:modelValue'
]);
const profile = useProfileStore();
const filter = ref('');
const loading = ref(true);
const selected = ref<Set<string>>(new Set(props.modelValue))
const groups = ref<Record<string, Group>>({});
const filtered = computed(() => {
return Object.keys(groups.value).filter((g) => {
return g.toLowerCase().includes(filter.value.toLowerCase());
});
})
onMounted(async () => {
await fetch();
});
function updateGroup(group: string) {
if (selected.value.has(group)) {
selected.value.delete(group)
} else {
if (props.limit && selected.value.size >= props.limit) {
throw new Error(`Cannot select more than ${props.limit} Channels`);
}
},
mounted: async function() {
await this.fetch();
},
methods: {
updateGroup: function(group) {
if (this.selected.has(group)) {
this.selected.delete(group)
} else {
this.selected.add(group)
}
this.$emit('update:modelValue', Array.from(this.selected));
},
fetch: async function() {
this.loading.groups = true;
let list;
if (this.connection) {
const url = stdurl(`/api/connection/${this.connection}/channel`);
list = await std(url);
} else {
const url = stdurl('/api/marti/group');
list = await std(url);
}
const channels = {};
JSON.parse(JSON.stringify(list.data)).sort((a, b) => {
return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);
}).forEach((channel) => {
if (channels[channel.name]) {
channels[channel.name].direction.push(channel.direction);
} else {
channel.direction = [channel.direction];
channels[channel.name] = channel;
}
});
this.groups = channels;
this.loading.groups = false;
},
selected.value.add(group)
}
emit('update:modelValue', Array.from(selected.value));
}
async function fetch() {
loading.value = true;
let list: Group[];
if (props.connection) {
const url = stdurl(`/api/connection/${props.connection}/channel`);
list = (await std(url) as {
data: Group[]
}).data;
} else {
list = await profile.loadChannels();
}
const channels: Record<string, Group> = {};
JSON.parse(JSON.stringify(list)).sort((a: Group, b: Group) => {
return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);
}).forEach((channel: Group) => {
if (channels[channel.name]) {
// @ts-expect-error Need to make these human readable strings instead of array to sync with type
channels[channel.name].direction.push(channel.direction);
} else {
// @ts-expect-error Need to make these human readable strings instead of array to sync with type
channel.direction = [channel.direction];
channels[channel.name] = channel;
}
});
groups.value = channels;
loading.value = false;
}
</script>
Loading

0 comments on commit 8a57744

Please sign in to comment.