ApprovalsPage.vue 1.87 KB
<script setup>
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { Button, Card } from '@drama/ui'

import { api, getToken } from '../api.js'

const { t } = useI18n()
const router = useRouter()
const tasks = ref(null)
const error = ref('')

async function load() {
  tasks.value = (await api('GET', '/api/voko/approvals')).tasks
}

/**
 * @param {string} id
 * @param {'approve'|'reject'} decision
 */
async function decide(id, decision) {
  error.value = ''
  try {
    await api('POST', `/api/voko/approvals/${id}`, { decision })
    await load()
  } catch (err) {
    error.value = err instanceof Error ? err.message : String(err)
  }
}

onMounted(async () => {
  if (getToken() === null) {
    await router.push({ path: '/login', query: { next: '/approvals' } })
    return
  }
  await load()
})
</script>

<template>
  <h1>{{ t('approvals.title') }}</h1>
  <p>{{ t('approvals.intro') }}</p>
  <p v-if="error" role="alert">{{ error }}</p>
  <template v-if="tasks">
    <p v-if="tasks.length === 0">{{ t('approvals.empty') }}</p>
    <Card v-for="task in tasks" :key="task.id" class="voko-approval">
      <h2>{{ task.title }}</h2>
      <p>{{ t(`board.templates.${task.template}`) }} · {{ t('board.reward', { reward: task.reward }) }} · {{ t('board.deadline', { deadline: new Date(task.deadline).toLocaleString() }) }}</p>
      <p class="voko-spec">{{ task.spec }}</p>
      <div class="voko-actions">
        <Button @click="decide(task.id, 'approve')">{{ t('approvals.approve') }}</Button>
        <Button variant="ghost" @click="decide(task.id, 'reject')">{{ t('approvals.reject') }}</Button>
      </div>
    </Card>
  </template>
</template>

<style scoped>
.voko-approval {
  margin-bottom: var(--drama-space-md);
}
.voko-spec {
  white-space: pre-wrap;
}
.voko-actions {
  display: flex;
  gap: var(--drama-space-sm);
}
</style>