@@ -20,6 +20,7 @@ import { | |||
GetRewardFundResponse, | |||
GetRewardFundsRequest, | |||
GetRewardFundsResponse, | |||
GetUsersResponse, | |||
LoginResponse, | |||
NearlyCompleteFundsRequest, | |||
NearlyCompleteFundsResponse, | |||
@@ -31,6 +32,8 @@ import { | |||
const controller = new SignetRequestController(); | |||
export const usersExist = () => controller.post<SuccessResponse, null>('/UsersExist', null); | |||
export const register = (username: string, password: string) => controller.post<SuccessResponse, AuthenticationRequest>('Register', { | |||
username, | |||
password, | |||
@@ -128,3 +131,5 @@ export const distributeRewardFund = (rewardFundID: number, payments: RewardDistr | |||
payments, | |||
distribute, | |||
}); | |||
export const getUsers = () => controller.post<GetUsersResponse, null>('GetUsers', null); |
@@ -208,3 +208,13 @@ export interface DistributeRewardsRequest { | |||
payments: RewardDistributionInfo[]; | |||
distribute: boolean; | |||
} | |||
export interface User { | |||
username: string, | |||
password: string, | |||
admin: number, | |||
} | |||
export interface GetUsersResponse { | |||
users: User[]; | |||
} |
@@ -13,10 +13,11 @@ import HomeView from '@/views/HomeView.vue'; | |||
import FundView from '@/views/FundView.vue'; | |||
import AddFundView from '@/views/AddFundView.vue'; | |||
import hasPermission from '@/lib/auth'; | |||
import SignetRequestController from '@/api/requests'; | |||
import AdminView from '@/views/AdminView.vue'; | |||
import ModifyQueueView from '@/views/ModifyQueueView.vue'; | |||
import AdminDashboardView from '@/views/AdminDashboardView.vue'; | |||
import { usersExist } from '@/api/composed'; | |||
import ModifyUserView from '@/views/ModifyUserView.vue'; | |||
const routes: Array<RouteRecordRaw> = [ | |||
{ | |||
@@ -44,8 +45,7 @@ const routes: Array<RouteRecordRaw> = [ | |||
meta: { | |||
requiredRights: Privileges.AdminPlus, | |||
accessible: async () => { | |||
const controller = new SignetRequestController(); | |||
const canProceed = await controller.post<SuccessResponse, null>('/UsersExist', null); | |||
const canProceed = await usersExist(); | |||
return canProceed?.success; | |||
}, | |||
title: 'Register', | |||
@@ -102,6 +102,24 @@ const routes: Array<RouteRecordRaw> = [ | |||
title: 'Add Group Fund', | |||
}, | |||
}, | |||
{ | |||
path: 'adduser', | |||
name: 'adduser', | |||
component: RegisterView, | |||
meta: { | |||
requiredRights: Privileges.AdminPlus, | |||
title: 'Add User', | |||
}, | |||
}, | |||
{ | |||
path: 'modifyuser', | |||
name: 'modifyuser', | |||
component: ModifyUserView, | |||
meta: { | |||
requiredRights: Privileges.AdminPlus, | |||
title: 'Modify User', | |||
}, | |||
}, | |||
], | |||
meta: { | |||
requiredRights: Privileges.Admin, | |||
@@ -3,12 +3,15 @@ | |||
<section class="section is-small px-0"> | |||
<template v-if="nearlyCompletedFunds.length > 0"> | |||
<div class="title is-4 has-text-white-ter has-text-centered">Nearly Completed Funds</div> | |||
<div v-for="(fund, ind) in nearlyCompletedFunds" :key="ind"> | |||
<div v-for="(fund, ind) in nearlyCompletedFunds" :key="ind" class="my-3"> | |||
<RouterLink :to="`/fund/${fund.id}`"> | |||
<FundLink :fund="fund" :aside="`${fund.amountAvailable} remaining`"/> | |||
</RouterLink> | |||
</div> | |||
</template> | |||
<template v-else> | |||
No funds are nearly complete. | |||
</template> | |||
</section> | |||
</div> | |||
</template> | |||
@@ -29,14 +29,18 @@ const links = [ | |||
text: 'Add Fund', | |||
to: '/admin/addfund', | |||
}, | |||
{ | |||
text: 'Modify Fund', | |||
to: '/admin/modifyfund', | |||
}, | |||
{ | |||
text: 'Add/Modify Queue', | |||
to: '/admin/modifyqueue', | |||
}, | |||
{ | |||
text: 'Add User', | |||
to: '/admin/adduser', | |||
}, | |||
{ | |||
text: 'Modify User', | |||
to: '/admin/modifyuser', | |||
}, | |||
]; | |||
</script> | |||
@@ -0,0 +1,83 @@ | |||
<template> | |||
<section class="section"> | |||
<table> | |||
<tr> | |||
<th> | |||
Username | |||
</th> | |||
<th> | |||
Password | |||
</th> | |||
<th> | |||
Privileges | |||
</th> | |||
</tr> | |||
<tr v-for="user in users" :key="user.username"> | |||
<td>{{ user.username }}</td> | |||
<td> | |||
<template v-if="userData.username === user.username || userData.privileges < 2"> | |||
<input type="password" | |||
class="input is-small" :aria-label="`${user.username}'s Password`"> | |||
</template> | |||
<template v-else> | |||
******** | |||
</template> | |||
</td> | |||
<td> | |||
<select class="select is-small" name="" id="" aria-label="User Privilege"> | |||
<option :value="privilege" | |||
:selected="getPrivilege(user.admin) === privilege" | |||
v-for="(privilege, i) in Object.values(privileges)" :key="i"> | |||
{{ privilege }} | |||
</option> | |||
</select> | |||
</td> | |||
</tr> | |||
</table> | |||
</section> | |||
</template> | |||
<script setup lang="ts"> | |||
import { | |||
Claims, | |||
Privileges, | |||
User, | |||
} from '@/api/types'; | |||
import { ref } from 'vue'; | |||
import { getUsers } from '@/api/composed'; | |||
import jwtDecode from 'jwt-decode'; | |||
import store from '@/store'; | |||
const users = ref<User[]>(); | |||
const resp = await getUsers(); | |||
users.value = resp?.users; | |||
const userData = ref<Claims>({ | |||
username: '', | |||
privileges: -1, | |||
exp: -1, | |||
}); | |||
userData.value = jwtDecode<Claims>(store.getters.getToken); | |||
const getPrivilege = (privilege: number) => Privileges[privilege]; | |||
const getPrivileges = () => Object.fromEntries( | |||
Object.entries(Privileges) | |||
.filter((p) => /^[0-9]+$/.test(p[1].toString())) | |||
.map((p) => p.reverse()), | |||
); | |||
const privileges = getPrivileges(); | |||
</script> | |||
<style scoped lang="stylus"> | |||
table | |||
width 100% | |||
table th | |||
color #bdbdbd | |||
table td | |||
font-family monospace | |||
</style> |