Skip to content

Commit

Permalink
Re-organized admin panels, add invite panel, add admin nav, and fix u…
Browse files Browse the repository at this point in the history
…p data table's null filter handling.
  • Loading branch information
MelissaAutumn committed May 23, 2024
1 parent 3785908 commit f99ed4f
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 39 deletions.
4 changes: 2 additions & 2 deletions backend/src/appointment/routes/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@


@router.get('/', response_model=list[schemas.Invite])
def get_all_invites(db: Session = Depends(get_db), admin: Subscriber = Depends(get_admin_subscriber)):
def get_all_invites(db: Session = Depends(get_db), _admin: Subscriber = Depends(get_admin_subscriber)):
"""List all existing invites, needs admin permissions"""
return db.query(models.Invite).all()


@router.post("/generate/{n}", response_model=list[schemas.Invite])
def generate_invite_codes(n: int, db: Session = Depends(get_db), admin: Subscriber = Depends(get_admin_subscriber)):
def generate_invite_codes(n: int, db: Session = Depends(get_db), _admin: Subscriber = Depends(get_admin_subscriber)):
"""endpoint to generate n invite codes, needs admin permissions"""
return repo.invite.generate_codes(db, n)

Expand Down
25 changes: 18 additions & 7 deletions frontend/src/components/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</label>
</div>
<list-pagination
:list-length="mutableDataList.length > 0 ? mutableDataList.length : dataList.length"
:list-length="totalDataLength"
:page-size="pageSize"
@update="updatePage"
/>
Expand Down Expand Up @@ -41,9 +41,9 @@
<a :href="fieldData.link" target="_blank">{{ fieldData.value }}</a>
</span>
<span v-else-if="fieldData.type === tableDataType.button">
<primary-button v-if="fieldData.buttonType === tableDataButtonType.primary" @click="emit('fieldClick', fieldKey, datum)">{{ fieldData.value }}</primary-button>
<secondary-button v-else-if="fieldData.buttonType === tableDataButtonType.secondary" @click="emit('fieldClick', fieldKey, datum)">{{ fieldData.value }}</secondary-button>
<caution-button v-else-if="fieldData.buttonType === tableDataButtonType.caution" @click="emit('fieldClick', fieldKey, datum)">{{ fieldData.value }}</caution-button>
<primary-button v-if="fieldData.buttonType === tableDataButtonType.primary" :disabled="fieldData.disabled" @click="emit('fieldClick', fieldKey, datum)">{{ fieldData.value }}</primary-button>
<secondary-button v-else-if="fieldData.buttonType === tableDataButtonType.secondary" :disabled="fieldData.disabled" @click="emit('fieldClick', fieldKey, datum)">{{ fieldData.value }}</secondary-button>
<caution-button v-else-if="fieldData.buttonType === tableDataButtonType.caution" :disabled="fieldData.disabled" @click="emit('fieldClick', fieldKey, datum)">{{ fieldData.value }}</caution-button>
</span>
</td>
</tr>
Expand Down Expand Up @@ -122,13 +122,13 @@ const updatePage = (index) => {
const columnSpan = computed(() => (columns.value.length + (allowMultiSelect.value ? 1 : 0)));
const selectedFields = ref([]);
const mutableDataList = ref([]);
const mutableDataList = ref(null);
/**
* Returns either a filtered data list, or the original all nice and paginated
*/
const paginatedDataList = computed(() => {
if (mutableDataList?.value?.length) {
if (mutableDataList?.value !== null) {
return mutableDataList.value.slice(currentPage.value * pageSize, (currentPage.value + 1) * pageSize);
}
if (dataList?.value?.length) {
Expand All @@ -138,6 +138,16 @@ const paginatedDataList = computed(() => {
return [];
});
const totalDataLength = computed(() => {
if (mutableDataList?.value !== null) {
return mutableDataList.value?.length ?? 0;
}
if (dataList?.value?.length) {
return dataList.value.length;
}
return 0;
});
const onFieldSelect = (evt, fieldData) => {
const isChecked = evt?.target?.checked;
Expand All @@ -154,8 +164,9 @@ const onFieldSelect = (evt, fieldData) => {
const onColumnFilter = (evt, filter) => {
mutableDataList.value = filter.fn(evt.target.value, dataList.value);
console.log('Data list info: ', mutableDataList.value, ' vs ', dataList.value);
if (mutableDataList.value === dataList.value) {
mutableDataList.value = [];
mutableDataList.value = null;
}
};
Expand Down
42 changes: 14 additions & 28 deletions frontend/src/elements/ListPagination.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<nav class="flex items-center flex-nowrap gap-2" :class="{ 'hidden': !listLength }">
<nav class="flex flex-nowrap items-center gap-2">
<button @click="prev" :disabled="isFirstPage" :class="{ 'text-gray-500': isFirstPage }">
<icon-chevron-left class="w-5 h-5 stroke-1.5" />
<icon-chevron-left class="stroke-1.5 size-5" />
</button>
<div
v-for="(p, i) in pageCount" :key="i"
Expand All @@ -19,7 +19,7 @@
<div v-show="showLastEllipsis(p)">&hellip;</div>
</div>
<button @click="next" :disabled="isLastPage" :class="{ 'text-gray-500': isLastPage }">
<icon-chevron-right class="w-5 h-5 stroke-1.5" />
<icon-chevron-right class="stroke-1.5 size-5" />
</button>
</nav>
</template>
Expand All @@ -39,19 +39,13 @@ const props = defineProps({
listLength: Number, // number of total items in the displayed list
pageSize: Number, // number of items per page
});
const emit = defineEmits(['update'])
const emit = defineEmits(['update']);
const currentPage = ref(0); // index of active page
const isFirstPage = computed(() => {
return currentPage.value <= 0;
});
const pageCount = computed(() => {
return Math.ceil(props.listLength/props.pageSize);
});
const isLastPage = computed(() => {
return currentPage.value >= pageCount.value-1;
});
const isFirstPage = computed(() => currentPage.value <= 0);
const pageCount = computed(() => Math.ceil(props.listLength / props.pageSize) || 1);
const isLastPage = computed(() => currentPage.value >= pageCount.value - 1);
const prev = () => {
if (!isFirstPage.value) {
Expand All @@ -70,23 +64,15 @@ const goto = (index) => {
emit('update', currentPage.value);
};
const showPageItem = (p) => {
return pageCount.value < 6 || p == 1 || p == 2 || isVisibleInnerPage(p) || p == pageCount.value-1;
};
const showFirstEllipsis = (p) => {
return pageCount.value >= 6 && currentPage.value > 2 && p == 2;
};
const showPageItemLink = (p) => {
return pageCount.value < 6 || p == 1 || isVisibleInnerPage(p);
};
const showLastEllipsis = (p) => {
return pageCount.value >= 6 && currentPage.value < pageCount.value-3 && p == pageCount.value-1;
};
const showPageItem = (p) => pageCount.value < 6 || p == 1 || p == 2 || isVisibleInnerPage(p) || p == pageCount.value - 1;
const showFirstEllipsis = (p) => pageCount.value >= 6 && currentPage.value > 2 && p == 2;
const showPageItemLink = (p) => pageCount.value < 6 || p == 1 || isVisibleInnerPage(p);
const showLastEllipsis = (p) => pageCount.value >= 6 && currentPage.value < pageCount.value - 3 && p == pageCount.value - 1;
const isVisibleInnerPage = (p) => (currentPage.value == 0 && p == 3)
|| ((currentPage.value == 0 || currentPage.value == 1) && p == 4)
|| (p > currentPage.value-1 && p < currentPage.value+3)
|| ((currentPage.value == pageCount.value-1 || currentPage.value == pageCount.value-2) && p == pageCount.value-3)
|| (currentPage.value == pageCount.value-1 && p == pageCount.value-2)
|| (p > currentPage.value - 1 && p < currentPage.value + 3)
|| ((currentPage.value == pageCount.value - 1 || currentPage.value == pageCount.value - 2) && p == pageCount.value - 3)
|| (currentPage.value == pageCount.value - 1 && p == pageCount.value - 2)
|| p == pageCount.value;
</script>
27 changes: 27 additions & 0 deletions frontend/src/elements/admin/AdminNav.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<div class="mb-4 flex h-12 justify-center gap-8">
<nav-bar-item
v-for="item in navItems"
:key="item"
:active="route.name == item"
:label="t(`label.${item}`)"
:link-name="item"
/>
</div>
</template>

<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import NavBarItem from '@/elements/NavBarItem.vue';
const { t } = useI18n();
const navItems = [
'admin-invite-codes-panel',
'admin-subscriber-panel',
];
const route = useRoute();
</script>
2 changes: 2 additions & 0 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"slotIsAvailableAgain": "Time slot now available for bookings."
},
"label": {
"admin-invite-codes-panel": "Invites",
"admin-subscriber-panel": "Subscribers",
"12hAmPm": "12h AM/PM",
"24h": "24h",
"DDMMYYYY": "DD/MM/YYYY",
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const AppointmentsView = defineAsyncComponent(() => import('@/views/Appointments
const SettingsView = defineAsyncComponent(() => import('@/views/SettingsView'));
const ProfileView = defineAsyncComponent(() => import('@/views/ProfileView'));
const LegalView = defineAsyncComponent(() => import('@/views/LegalView'));
const SubscriberPanelView = defineAsyncComponent(() => import('@/views/SubscriberPanelView'));
const SubscriberPanelView = defineAsyncComponent(() => import('@/views/admin/SubscriberPanelView'));
const InviteCodePanelView = defineAsyncComponent(() => import('@/views/admin/InviteCodePanelView.vue'));

/**
* Defined routes for Thunderbird Appointment
Expand Down Expand Up @@ -91,11 +92,17 @@ const routes = [
name: 'terms',
component: LegalView,
},
// Admin
{
path: '/admin/subscribers',
name: 'admin-subscriber-panel',
component: SubscriberPanelView,
},
{
path: '/admin/invites',
name: 'admin-invite-codes-panel',
component: InviteCodePanelView,
},
];

// create router object to export
Expand Down
Loading

0 comments on commit f99ed4f

Please sign in to comment.