jobhunt-platform/apps/web/src/views/TodayView.vue

122 lines
No EOL
4.1 KiB
Vue

<script setup lang="ts">
import { onMounted, ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useToastStore } from '@/stores/toast'
import * as api from '@/api'
import CostDisplay from '@/components/CostDisplay.vue'
import type { TodayResponse } from '@/types'
const toast = useToastStore()
const router = useRouter()
const today = ref<TodayResponse | null>(null)
const loading = ref(true)
const nudgeIds = computed(() => new Set(today.value?.nudges.map((n) => n.application_id) ?? []))
async function loadToday() {
try {
today.value = await api.getToday()
} catch {
toast.push('Failed to load today digest', 'error')
} finally {
loading.value = false
}
}
function goToApplication(id: string) {
router.push(`/applications/${id}`)
}
function copyNudge(suggestion: string) {
if (navigator.clipboard) {
navigator.clipboard.writeText(suggestion).then(
() => toast.push('Follow-up draft copied to clipboard', 'success'),
() => toast.push('Copy failed', 'error')
)
} else {
toast.push('Clipboard not available', 'error')
}
}
onMounted(loadToday)
</script>
<template>
<div class="space-y-6">
<h1 class="text-2xl font-bold">Today</h1>
<div v-if="loading" class="text-gray-500">Loading...</div>
<template v-if="!loading && today">
<!-- Pending approvals banner -->
<div v-if="today.pending_approvals > 0" class="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<span class="font-medium text-yellow-800">
{{ today.pending_approvals }} pending approval{{ today.pending_approvals > 1 ? 's' : '' }} waiting for you.
</span>
</div>
<!-- Digest cards -->
<section>
<h2 class="font-semibold text-lg mb-3">Top Matches Today</h2>
<div v-if="today.digest.length === 0" class="text-gray-400 text-sm">No postings in your digest yet.</div>
<div v-else class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
<div
v-for="item in today.digest"
:key="item.application_id"
class="bg-white rounded-lg border border-gray-200 p-4 cursor-pointer hover:shadow-md transition-shadow"
@click="goToApplication(item.application_id)"
>
<div class="font-medium">{{ item.title }}</div>
<div class="text-sm text-gray-600">{{ item.company }}</div>
<div class="mt-2 flex items-center gap-2">
<span class="text-xs bg-green-100 text-green-800 rounded px-2 py-0.5 font-medium">
Score: {{ item.score }}
</span>
<span
v-if="nudgeIds.has(item.application_id)"
class="inline-block w-2 h-2 rounded-full bg-orange-500"
title="Follow-up nudge pending"
></span>
</div>
</div>
</div>
</section>
<!-- Nudge cards -->
<section>
<h2 class="font-semibold text-lg mb-3">Follow-up Nudges</h2>
<div v-if="today.nudges.length === 0" class="text-gray-400 text-sm">No nudges. You are up to date.</div>
<div v-else class="space-y-3">
<div
v-for="nudge in today.nudges"
:key="nudge.application_id"
class="bg-orange-50 border border-orange-200 rounded-lg p-4"
>
<div class="flex items-center justify-between">
<span class="font-medium text-orange-900">
Sent {{ nudge.days_since_sent }} days ago
</span>
<button
class="text-sm text-indigo-600 hover:underline"
@click="goToApplication(nudge.application_id)"
>
Open application
</button>
</div>
<p class="text-sm text-gray-700 mt-2">{{ nudge.suggestion }}</p>
<button
class="mt-2 text-sm bg-indigo-600 text-white px-3 py-1 rounded hover:bg-indigo-700"
@click="copyNudge(nudge.suggestion)"
>
Copy follow-up draft
</button>
</div>
</div>
</section>
<!-- Cost display -->
<CostDisplay />
</template>
</div>
</template>