Skip to content

Commit

Permalink
Migrate Mission List to TS
Browse files Browse the repository at this point in the history
  • Loading branch information
ingalls committed Nov 3, 2024
1 parent 0d909df commit 3ae68b0
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 180 deletions.
6 changes: 3 additions & 3 deletions api/web/src/components/CloudTAK/Menu/Channels.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/>
</div>

<NoChannelsInfo v-if='hasNoChannels' />
<EmptyInfo v-if='hasNoChannels' />

<TablerLoading v-if='loading' />
<TablerNone
Expand Down Expand Up @@ -105,7 +105,7 @@ import {
TablerLoading
} from '@tak-ps/vue-tabler';
import MenuTemplate from '../util/MenuTemplate.vue';
import NoChannelsInfo from '../util/NoChannelsInfo.vue';
import EmptyInfo from '../util/EmptyInfo.vue';
import {
IconLocation,
IconLocationOff,
Expand Down Expand Up @@ -191,7 +191,7 @@ export default {
IconEyeOff,
IconLocation,
IconLocationOff,
NoChannelsInfo,
EmptyInfo,
IconRefresh,
TablerNone,
TablerInput,
Expand Down
6 changes: 3 additions & 3 deletions api/web/src/components/CloudTAK/Menu/Contacts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/>
</div>

<NoChannelsInfo v-if='hasNoChannels' />
<EmptyInfo v-if='hasNoChannels' />

<TablerLoading v-if='loading' />
<TablerNone
Expand Down Expand Up @@ -77,14 +77,14 @@ import {
TablerIconButton
} from '@tak-ps/vue-tabler';
import Contact from '../util/Contact.vue';
import NoChannelsInfo from '../util/NoChannelsInfo.vue';
import EmptyInfo from '../util/EmptyInfo.vue';
export default {
name: 'CloudTAKContacts',
components: {
Contact,
TablerNone,
NoChannelsInfo,
EmptyInfo,
TablerInput,
TablerLoading,
TablerIconButton,
Expand Down
212 changes: 94 additions & 118 deletions api/web/src/components/CloudTAK/Menu/Missions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,14 @@
:loading='loading'
>
<template #buttons>
<IconPlus
v-tooltip='"Create Sync"'
:size='32'
:stroke='1'
class='cursor-pointer'
<TablerIconButton
title='Create Sync'
@click='create = true'
/>
<IconRefresh
v-tooltip='"Refresh"'
:size='32'
:stroke='1'
class='cursor-pointer'
><IconPlus :size='32' :stroke='1'/></TablerIconButton>
<TablerIconButton
title='Refresh'
@click='fetchMissions'
/>
> <IconRefresh :size='32' :stroke='1' /></TablerIconButton>
</template>
<template #default>
<div class='col-12 px-2 py-2'>
Expand All @@ -30,16 +24,16 @@

<ChannelInfo />

<NoChannelsInfo v-if='hasNoChannels' />
<EmptyInfo v-if='hasNoChannels' />

<TablerNone
v-if='!list.data.length'
:create='false'
label='Data Sync'
/>
<TablerAlert
v-else-if='err'
:err='err'
v-else-if='error'
:err='error'
/>
<template v-else>
<div
Expand Down Expand Up @@ -125,22 +119,26 @@
@click='create = false'
/>
<MissionCreate
@mission='$router.push(`/menu/missions/${$event.guid}`)'
@mission='router.push(`/menu/missions/${$event.guid}`)'
@close='create = false'
/>
</TablerModal>
</template>

<script>
<script setup lang='ts'>
import { ref, onMounted, computed } from 'vue';
import MissionCreate from './Mission/MissionCreate.vue';
import MenuTemplate from '../util/MenuTemplate.vue';
import { useRouter } from 'vue-router';
import {
TablerIconButton,
TablerInput,
TablerNone,
TablerAlert,
TablerModal
} from '@tak-ps/vue-tabler';
import { std, stdurl } from '/src/std.ts';
import type { MissionList } from '../../../../src/types.ts';
import { std, stdurl } from '../../../../src/std.ts';
import {
IconPlus,
IconLock,
Expand All @@ -149,115 +147,93 @@ import {
IconRefresh,
} from '@tabler/icons-vue';
import ChannelInfo from '../util/ChannelInfo.vue';
import { mapGetters } from 'pinia'
import { useProfileStore } from '/src/stores/profile.ts';
import NoChannelsInfo from '../util/NoChannelsInfo.vue';
import { useMapStore } from '/src/stores/map.ts';
import { useProfileStore } from '../../../../src/stores/profile.ts';
import EmptyInfo from '../util/EmptyInfo.vue';
import { useMapStore } from '../../../../src/stores/map.ts';
import Mission from '../../../../src/stores/base/mission.ts';
const mapStore = useMapStore();
export default {
name: 'CloudTAKMissions',
components: {
NoChannelsInfo,
ChannelInfo,
MenuTemplate,
TablerNone,
TablerAlert,
TablerInput,
TablerModal,
MissionCreate,
IconPlus,
IconLock,
IconLockOpen,
IconAccessPoint,
IconRefresh,
},
data: function() {
return {
err: false,
create: false,
loading: true,
subscribed: new Set(),
errors: {},
paging: {
filter: ''
},
list: {
data: [],
const error = ref<Error | undefined>();
const create = ref(false)
const loading = ref(true)
const subscribed = ref(new Set());
const errors = ref<Record<string, string | undefined>>({})
const router = useRouter();
const paging = ref({ filter: '' });
const list = ref<MissionList>({ data: [], });
onMounted(async () => {
await fetchMissions();
});
const hasNoChannels = computed(() => useProfileStore.hasNoChannels);
const filteredList = computed(() => {
return list.value.data.filter((mission) => {
return mission.name.toLowerCase()
.includes(paging.value.filter.toLowerCase());
})
});
const filteredListRemainder = computed(() => {
return filteredList.filter((mission) => {
return !subscribed.value.has(mission.guid)
})
});
const filteredListSubscribed = computed(() => {
return filteredList.filter((mission) => {
return subscribed.value.has(mission.guid)
})
});
async function openMission(mission, usePassword) {
if (mission.passwordProtected && subscribed.value.has(mission.guid)) {
const o = mapStore.getOverlayByMode('mission', mission.guid);
router.push(`/menu/missions/${mission.guid}?token=${encodeURIComponent(o.token)}`);
} else if (mission.passwordProtected && usePassword) {
try {
const getMission = await fetchMission(mission, mission.password);
router.push(`/menu/missions/${mission.guid}?token=${encodeURIComponent(getMission.token)}`);
} catch (err) {
if (err.message.includes('Illegal attempt to access mission')) {
errors.value[mission.guid] = 'Invalid Password';
} else {
errors.value[mission.guid] = err.message;
}
};
},
mounted: async function() {
await this.fetchMissions();
},
computed: {
...mapGetters(useProfileStore, ['hasNoChannels']),
filteredList: function() {
return this.list.data.filter((mission) => {
return mission.name.toLowerCase()
.includes(this.paging.filter.toLowerCase());
})
},
filteredListRemainder: function() {
return this.filteredList.filter((mission) => {
return !this.subscribed.has(mission.guid)
})
},
filteredListSubscribed: function() {
return this.filteredList.filter((mission) => {
return this.subscribed.has(mission.guid)
})
}
},
methods: {
openMission: async function(mission, usePassword) {
if (mission.passwordProtected && this.subscribed.has(mission.guid)) {
const o = mapStore.getOverlayByMode('mission', mission.guid);
this.$router.push(`/menu/missions/${mission.guid}?token=${encodeURIComponent(o.token)}`);
} else if (mission.passwordProtected && usePassword) {
try {
const getMission = await this.fetchMission(mission, mission.password);
this.$router.push(`/menu/missions/${mission.guid}?token=${encodeURIComponent(getMission.token)}`);
} catch (err) {
if (err.message.includes('Illegal attempt to access mission')) {
this.errors[mission.guid] = 'Invalid Password';
} else {
this.errors[mission.guid] = err.message;
}
}
} else if (mission.passwordProtected && mission.password === undefined) {
mission.password = '';
} else if (!mission.passwordProtected) {
this.$router.push(`/menu/missions/${mission.guid}?password=${encodeURIComponent(mission.password)}`);
}
},
fetchMission: async function(mission, password) {
const url = stdurl(`/api/marti/missions/${mission.guid}`);
if (password) url.searchParams.append('password', password);
} else if (mission.passwordProtected && mission.password === undefined) {
mission.password = '';
} else if (!mission.passwordProtected) {
router.push(`/menu/missions/${mission.guid}?password=${encodeURIComponent(mission.password)}`);
}
}
async function fetchMission(mission, password) {
const url = stdurl(`/api/marti/missions/${mission.guid}`);
if (password) url.searchParams.append('password', password);
const m = await std(url);
return m;
},
fetchMissions: async function() {
this.err = false;
const m = await std(url);
return m;
}
async function fetchMissions() {
error.value = undefined;
try {
this.loading = true;
const url = stdurl('/api/marti/mission');
url.searchParams.append('passwordProtected', 'true');
url.searchParams.append('defaultRole', 'true');
this.list = await std(url);
try {
loading.value = true;
for (const item of this.list.data) {
if (mapStore.getOverlayByMode('mission', item.guid)) {
this.subscribed.add(item.guid);
}
}
} catch (err) {
this.err = err;
list.value = await Mission.list();
for (const item of list.value.data) {
if (mapStore.getOverlayByMode('mission', item.guid)) {
subscribed.value.add(item.guid);
}
this.loading = false;
}
} catch (err) {
error.value = err instanceof Error ? err : new Error(String(err));
}
loading.value = false;
}
</script>
6 changes: 3 additions & 3 deletions api/web/src/components/CloudTAK/Menu/Packages.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
</div>

<ChannelInfo label='Data Packages' />
<NoChannelsInfo v-if='hasNoChannels' />
<EmptyInfo v-if='hasNoChannels' />

<TablerAlert
v-if='err'
Expand Down Expand Up @@ -104,7 +104,7 @@ import {
} from '@tabler/icons-vue';
import timeDiff from '../../../timediff.ts';
import ChannelInfo from '../util/ChannelInfo.vue';
import NoChannelsInfo from '../util/NoChannelsInfo.vue';
import EmptyInfo from '../util/EmptyInfo.vue';
import Upload from '../../util/Upload.vue';
import { useProfileStore } from '/src/stores/profile.ts';
import { mapGetters } from 'pinia'
Expand All @@ -115,7 +115,7 @@ export default {
Upload,
IconPlus,
IconRefresh,
NoChannelsInfo,
EmptyInfo,
ChannelInfo,
TablerInput,
TablerAlert,
Expand Down
37 changes: 37 additions & 0 deletions api/web/src/components/CloudTAK/util/EmptyInfo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<template>
<div class='col-12 d-flex py-2 px-2 text-red justify-content-center'>
<div class='d-flex align-items-center'>
<IconAlertCircle
:size='32'
stroke='2'
class='mx-2'
/>
<div>
<span v-if='type === InfoType.CHANNELS'>
No Channels are selected
</span>
<span v-else-if='type === InfoType.MISSIONS'>
No Data Syncs have been subscribed to
</span>
</div>
</div>
</div>
</template>

<script setup lang='ts'>
import {
IconAlertCircle
} from '@tabler/icons-vue';
enum InfoType {
CHANNELS = 'Channels',
MISSIONS = 'missions'
};
const props = defineProps({
type: {
type: String,
default: InfoType.CHANNELS
}
})
</script>
Loading

0 comments on commit 3ae68b0

Please sign in to comment.