init
This commit is contained in:
commit
5782fc7832
|
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "jobs",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"sass": "^1.86.0",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,423 @@
|
|||
<script setup>
|
||||
import JobCard from './components/JobCard.vue'
|
||||
import SideNav from './components/SideNav.vue'
|
||||
import Dashboard from './components/Dashboard.vue'
|
||||
import Applications from './components/Applications.vue'
|
||||
import SavedJobs from './components/SavedJobs.vue'
|
||||
import JobDetail from './components/JobDetail.vue'
|
||||
import JobFilters from './components/JobFilters.vue'
|
||||
import { ref, provide, onMounted, computed } from 'vue'
|
||||
|
||||
// Current active section
|
||||
const activeSection = ref('dashboard')
|
||||
|
||||
// Selected job for detail view
|
||||
const selectedJobId = ref(null)
|
||||
const showJobDetail = ref(false)
|
||||
|
||||
// Theme state
|
||||
const isDarkMode = ref(false)
|
||||
|
||||
// Initialize theme on mount
|
||||
onMounted(() => {
|
||||
// Check for saved preference in localStorage
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
if (savedTheme) {
|
||||
isDarkMode.value = savedTheme === 'dark'
|
||||
} else {
|
||||
// Use system preference as fallback
|
||||
isDarkMode.value = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
|
||||
// Apply initial theme
|
||||
applyTheme(isDarkMode.value)
|
||||
})
|
||||
|
||||
// Apply theme to document
|
||||
const applyTheme = (dark) => {
|
||||
document.documentElement.classList.toggle('dark-mode', dark)
|
||||
document.documentElement.classList.toggle('light-mode', !dark)
|
||||
localStorage.setItem('theme', dark ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
// Handle theme toggle from SideNav
|
||||
const handleThemeToggle = (dark) => {
|
||||
isDarkMode.value = dark
|
||||
applyTheme(dark)
|
||||
}
|
||||
|
||||
// Provide theme state to components
|
||||
provide('isDarkMode', isDarkMode)
|
||||
provide('toggleTheme', handleThemeToggle)
|
||||
|
||||
// Provide navigation state to components
|
||||
provide('activeSection', activeSection)
|
||||
|
||||
// Handle navigation
|
||||
const handleNavigation = (section) => {
|
||||
activeSection.value = section
|
||||
console.log(`Navigated to: ${section}`)
|
||||
}
|
||||
|
||||
// Sample job listings data
|
||||
const allJobListings = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Frontend Developer',
|
||||
company: 'Tech Innovations Inc.',
|
||||
location: 'San Francisco, CA',
|
||||
salary: '$120,000 - $150,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '3+ years',
|
||||
description: 'We are looking for an experienced Frontend Developer proficient in Vue.js to join our growing team. You will be responsible for building user interfaces and implementing new features.',
|
||||
applied: false,
|
||||
tags: ['Vue.js', 'JavaScript', 'CSS', 'UI/UX']
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Backend Engineer',
|
||||
company: 'DataSystems Co.',
|
||||
location: 'Remote',
|
||||
salary: '$130,000 - $160,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '4+ years',
|
||||
description: 'Join our backend team to develop scalable APIs and services. Experience with Node.js and database design required.',
|
||||
applied: false,
|
||||
tags: ['Node.js', 'API', 'Backend', 'Databases']
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Full Stack Developer',
|
||||
company: 'WebSolutions Ltd.',
|
||||
location: 'New York, NY',
|
||||
salary: '$140,000 - $170,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '5+ years',
|
||||
description: 'Looking for a versatile developer who can work across the entire stack. Experience with Vue.js and Node.js is a plus.',
|
||||
applied: true,
|
||||
tags: ['Full Stack', 'Vue.js', 'Node.js', 'JavaScript']
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'UX/UI Designer',
|
||||
company: 'Creative Design Studio',
|
||||
location: 'Chicago, IL',
|
||||
salary: '$110,000 - $135,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '2+ years',
|
||||
description: 'Join our design team to create beautiful and intuitive user interfaces for web and mobile applications.',
|
||||
applied: false,
|
||||
tags: ['UI/UX', 'Design', 'Figma', 'Prototyping']
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'DevOps Engineer',
|
||||
company: 'CloudTech Solutions',
|
||||
location: 'Remote',
|
||||
salary: '$135,000 - $165,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '4+ years',
|
||||
description: 'Looking for a skilled DevOps engineer to help us build and maintain our cloud infrastructure and CI/CD pipelines.',
|
||||
applied: false,
|
||||
tags: ['AWS', 'Docker', 'Kubernetes', 'CI/CD']
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Product Manager',
|
||||
company: 'Tech Innovations Inc.',
|
||||
location: 'San Francisco, CA',
|
||||
salary: '$150,000 - $180,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '5+ years',
|
||||
description: 'Lead product development initiatives and work closely with engineering, design, and marketing teams to deliver exceptional products.',
|
||||
applied: false,
|
||||
tags: ['Product', 'Agile', 'Leadership', 'Strategy']
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: 'Data Scientist',
|
||||
company: 'DataSystems Co.',
|
||||
location: 'Boston, MA',
|
||||
salary: '$140,000 - $170,000',
|
||||
employmentType: 'Full-time',
|
||||
experience: '3+ years',
|
||||
description: 'Apply machine learning and statistical techniques to analyze large datasets and extract valuable insights for our clients.',
|
||||
applied: false,
|
||||
tags: ['Python', 'Machine Learning', 'Data Analysis', 'SQL']
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: 'Frontend Developer (Contract)',
|
||||
company: 'WebSolutions Ltd.',
|
||||
location: 'Remote',
|
||||
salary: '$90,000 - $120,000',
|
||||
employmentType: 'Contract',
|
||||
experience: '2+ years',
|
||||
description: 'Short-term contract role for a Vue.js developer to help us complete a client project over the next 6 months.',
|
||||
applied: false,
|
||||
tags: ['Vue.js', 'JavaScript', 'Contract', 'Remote']
|
||||
}
|
||||
];
|
||||
|
||||
// Filtered job listings
|
||||
const filteredJobListings = ref([...allJobListings]);
|
||||
|
||||
// Handle job application
|
||||
const handleJobApplication = (jobTitle) => {
|
||||
console.log(`Applied for: ${jobTitle}`);
|
||||
};
|
||||
|
||||
// View job details
|
||||
const viewJobDetails = (jobId) => {
|
||||
selectedJobId.value = jobId;
|
||||
showJobDetail.value = true;
|
||||
};
|
||||
|
||||
// Handle filter changes
|
||||
const handleFilterChange = (filteredJobs) => {
|
||||
filteredJobListings.value = filteredJobs;
|
||||
};
|
||||
|
||||
// Close job details
|
||||
const closeJobDetails = () => {
|
||||
showJobDetail.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<SideNav @navigate="handleNavigation" />
|
||||
|
||||
<div class="content-wrapper">
|
||||
<header>
|
||||
<h1>{{ activeSection === 'dashboard' ? 'Dashboard' : activeSection === 'jobs' ? 'Job Board' : activeSection.charAt(0).toUpperCase() + activeSection.slice(1) }}</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section v-if="activeSection === 'jobs'" class="job-listings">
|
||||
<div v-if="!showJobDetail" class="jobs-container">
|
||||
<h2 class="section-heading">Available Positions</h2>
|
||||
|
||||
<!-- Job Filters Component -->
|
||||
<JobFilters
|
||||
:jobs="allJobListings"
|
||||
:initially-expanded="false"
|
||||
@filter-change="filteredJobListings = $event"
|
||||
/>
|
||||
|
||||
<div class="filter-results">
|
||||
<p class="results-count">{{ filteredJobListings.length }} job{{ filteredJobListings.length !== 1 ? 's' : '' }} found</p>
|
||||
</div>
|
||||
|
||||
<div class="job-cards" v-if="filteredJobListings.length > 0">
|
||||
<JobCard
|
||||
v-for="job in filteredJobListings"
|
||||
:key="job.id"
|
||||
:title="job.title"
|
||||
:company="job.company"
|
||||
:location="job.location"
|
||||
:salary="job.salary"
|
||||
:description="job.description"
|
||||
:applied="job.applied"
|
||||
:tags="job.tags"
|
||||
@apply="handleJobApplication"
|
||||
@click="viewJobDetails(job.id)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-results">
|
||||
<div class="no-results-icon">🔍</div>
|
||||
<h3>No jobs found</h3>
|
||||
<p>Try adjusting your search filters to find more opportunities.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<JobDetail
|
||||
v-if="showJobDetail"
|
||||
:jobId="selectedJobId"
|
||||
@close="closeJobDetails"
|
||||
@apply="handleJobApplication"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeSection === 'dashboard'" class="dashboard-section">
|
||||
<Dashboard />
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeSection === 'applications'" class="applications-section">
|
||||
<Applications />
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeSection === 'saved'" class="saved-section">
|
||||
<SavedJobs />
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeSection === 'profile'" class="profile">
|
||||
<h2>My Profile</h2>
|
||||
<p>Manage your profile information.</p>
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeSection === 'settings'" class="settings">
|
||||
<h2>Settings</h2>
|
||||
<p>Adjust your account settings.</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Job Board Application</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
margin-left: 240px;
|
||||
transition: margin-left 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
|
||||
.side-nav.collapsed + & {
|
||||
margin-left: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1.5rem 2rem;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem;
|
||||
|
||||
section {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.4rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.jobs-container {
|
||||
padding: 1.5rem;
|
||||
background-color: var(--bg-color, #f8f9fa);
|
||||
border-radius: 16px;
|
||||
|
||||
.section-heading {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2rem;
|
||||
color: var(--text-color, #333);
|
||||
}
|
||||
|
||||
.filter-results {
|
||||
margin: 1.5rem 0;
|
||||
|
||||
.results-count {
|
||||
font-size: 1rem;
|
||||
color: var(--text-color-secondary, #666);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.no-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3rem 1rem;
|
||||
text-align: center;
|
||||
background-color: var(--card-bg, #fff);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.no-results-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.4rem;
|
||||
margin: 0 0 0.5rem;
|
||||
color: var(--text-color, #333);
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--text-color-secondary, #666);
|
||||
max-width: 400px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.job-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.dashboard-section {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 1rem 0;
|
||||
margin-top: 2rem;
|
||||
border-top: 1px solid #eee;
|
||||
font-size: 0.9rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
header {
|
||||
border-bottom-color: #333;
|
||||
}
|
||||
|
||||
main section h2 {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top-color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.content-wrapper {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
@import './variables.scss';
|
||||
|
||||
// Reset and base styles
|
||||
:root {
|
||||
font-family: $font-family;
|
||||
line-height: $line-height;
|
||||
font-weight: $font-weight-normal;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
// Default dark theme
|
||||
:root, .dark-mode {
|
||||
color-scheme: dark;
|
||||
color: $text-color-dark;
|
||||
background-color: $bg-color-dark;
|
||||
|
||||
// CSS variables for theming
|
||||
--primary-color: #{$primary-color};
|
||||
--primary-hover-color: #{$primary-hover-color};
|
||||
--text-color: #{$text-color-dark};
|
||||
--bg-color: #{$bg-color-dark};
|
||||
--sidebar-bg: #1e1e1e;
|
||||
--sidebar-border: #333;
|
||||
--card-bg: #2a2a2a;
|
||||
--card-border: #444;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: $font-weight-medium;
|
||||
color: $primary-color;
|
||||
text-decoration: inherit;
|
||||
|
||||
&:hover {
|
||||
color: $primary-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: $font-weight-medium;
|
||||
font-family: inherit;
|
||||
background-color: $button-bg-dark;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
|
||||
&:hover {
|
||||
border-color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: $max-content-width;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
// Light mode theme
|
||||
.light-mode {
|
||||
color-scheme: light;
|
||||
color: $text-color-light;
|
||||
background-color: $bg-color-light;
|
||||
|
||||
// Update CSS variables for light mode
|
||||
--primary-color: #{$primary-color};
|
||||
--primary-hover-color: #{$primary-hover-color};
|
||||
--text-color: #{$text-color-light};
|
||||
--bg-color: #{$bg-color-light};
|
||||
--sidebar-bg: #fff;
|
||||
--sidebar-border: #eee;
|
||||
--card-bg: #fff;
|
||||
--card-border: #ddd;
|
||||
|
||||
button {
|
||||
background-color: $button-bg-light;
|
||||
}
|
||||
}
|
||||
|
||||
// System preference override
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root:not(.dark-mode):not(.light-mode) {
|
||||
color: $text-color-light;
|
||||
background-color: $bg-color-light;
|
||||
|
||||
// Update CSS variables for light mode
|
||||
--primary-color: #{$primary-color};
|
||||
--primary-hover-color: #{$primary-hover-color};
|
||||
--text-color: #{$text-color-light};
|
||||
--bg-color: #{$bg-color-light};
|
||||
--sidebar-bg: #fff;
|
||||
--sidebar-border: #eee;
|
||||
--card-bg: #fff;
|
||||
--card-border: #ddd;
|
||||
}
|
||||
|
||||
:root:not(.dark-mode):not(.light-mode) button {
|
||||
background-color: $button-bg-light;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Color variables
|
||||
$primary-color: #3498db;
|
||||
$primary-hover-color: #2980b9;
|
||||
$text-color-dark: rgba(255, 255, 255, 0.87);
|
||||
$text-color-light: #333;
|
||||
$bg-color-dark: #1a1a1a;
|
||||
$bg-color-light: #f8f8f8;
|
||||
$button-bg-dark: #2c3e50;
|
||||
$button-bg-light: #ecf0f1;
|
||||
|
||||
// Typography
|
||||
$font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
$font-weight-normal: 400;
|
||||
$font-weight-medium: 500;
|
||||
$line-height: 1.5;
|
||||
|
||||
// Layout
|
||||
$max-content-width: 1000px;
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
|
|
@ -0,0 +1,492 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
// Sample applications data
|
||||
const applications = ref([
|
||||
{
|
||||
id: 1,
|
||||
jobTitle: 'Frontend Developer',
|
||||
company: 'Tech Innovations Inc.',
|
||||
location: 'San Francisco, CA',
|
||||
appliedDate: '2025-03-15',
|
||||
status: 'Interview',
|
||||
nextStep: 'Technical Interview on April 5, 2025',
|
||||
notes: 'Prepare for React and Vue questions. Review portfolio projects.'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
jobTitle: 'Senior UI Developer',
|
||||
company: 'Digital Creations',
|
||||
location: 'Remote',
|
||||
appliedDate: '2025-03-18',
|
||||
status: 'Offer',
|
||||
nextStep: 'Review offer by April 10, 2025',
|
||||
notes: 'Salary negotiation pending. Discuss remote work policy.'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
jobTitle: 'Full Stack Engineer',
|
||||
company: 'WebSolutions Ltd.',
|
||||
location: 'New York, NY',
|
||||
appliedDate: '2025-03-20',
|
||||
status: 'Applied',
|
||||
nextStep: 'Waiting for response',
|
||||
notes: 'Follow up if no response by April 3.'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
jobTitle: 'Backend Developer',
|
||||
company: 'DataSystems Co.',
|
||||
location: 'Chicago, IL',
|
||||
appliedDate: '2025-03-22',
|
||||
status: 'Rejected',
|
||||
nextStep: 'N/A',
|
||||
notes: 'Received rejection email. Requested feedback.'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
jobTitle: 'Frontend Team Lead',
|
||||
company: 'InnovateTech Solutions',
|
||||
location: 'Boston, MA',
|
||||
appliedDate: '2025-03-25',
|
||||
status: 'Screening',
|
||||
nextStep: 'Phone screening on April 2, 2025',
|
||||
notes: 'Research company products before the call.'
|
||||
}
|
||||
]);
|
||||
|
||||
// Filter applications by status
|
||||
const statusFilter = ref('All');
|
||||
const statusOptions = ['All', 'Applied', 'Screening', 'Interview', 'Offer', 'Rejected'];
|
||||
|
||||
const filteredApplications = computed(() => {
|
||||
if (statusFilter.value === 'All') {
|
||||
return applications.value;
|
||||
}
|
||||
return applications.value.filter(app => app.status === statusFilter.value);
|
||||
});
|
||||
|
||||
// Application details modal
|
||||
const selectedApplication = ref(null);
|
||||
const showModal = ref(false);
|
||||
|
||||
const openApplicationDetails = (application) => {
|
||||
selectedApplication.value = application;
|
||||
showModal.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
showModal.value = false;
|
||||
};
|
||||
|
||||
// Add note to application
|
||||
const newNote = ref('');
|
||||
|
||||
const addNote = (application) => {
|
||||
if (newNote.value.trim()) {
|
||||
application.notes += '\n' + newNote.value.trim();
|
||||
newNote.value = '';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="applications-container">
|
||||
<!-- Filters -->
|
||||
<div class="filters">
|
||||
<div class="status-filter">
|
||||
<label for="status-select">Filter by Status:</label>
|
||||
<select id="status-select" v-model="statusFilter">
|
||||
<option v-for="option in statusOptions" :key="option" :value="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="search-applications">
|
||||
<input type="text" placeholder="Search applications..." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Applications Table -->
|
||||
<div class="applications-table-container">
|
||||
<table class="applications-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job Title</th>
|
||||
<th>Company</th>
|
||||
<th>Applied Date</th>
|
||||
<th>Status</th>
|
||||
<th>Next Step</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="application in filteredApplications" :key="application.id" :class="{ 'rejected': application.status === 'Rejected' }">
|
||||
<td class="job-title">{{ application.jobTitle }}</td>
|
||||
<td>{{ application.company }}</td>
|
||||
<td>{{ application.appliedDate }}</td>
|
||||
<td>
|
||||
<span class="status-badge" :class="application.status.toLowerCase()">
|
||||
{{ application.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ application.nextStep }}</td>
|
||||
<td>
|
||||
<button class="view-btn" @click="openApplicationDetails(application)">View Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="filteredApplications.length === 0">
|
||||
<td colspan="6" class="no-applications">No applications found matching the selected filter.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Application Details Modal -->
|
||||
<div class="modal-overlay" v-if="showModal" @click="closeModal">
|
||||
<div class="modal-content" @click.stop>
|
||||
<div class="modal-header">
|
||||
<h3>{{ selectedApplication?.jobTitle }}</h3>
|
||||
<button class="close-modal" @click="closeModal">×</button>
|
||||
</div>
|
||||
<div class="modal-body" v-if="selectedApplication">
|
||||
<div class="application-details">
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Company:</span>
|
||||
<span class="detail-value">{{ selectedApplication.company }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Location:</span>
|
||||
<span class="detail-value">{{ selectedApplication.location }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Applied Date:</span>
|
||||
<span class="detail-value">{{ selectedApplication.appliedDate }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Status:</span>
|
||||
<span class="detail-value status-badge" :class="selectedApplication.status.toLowerCase()">
|
||||
{{ selectedApplication.status }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Next Step:</span>
|
||||
<span class="detail-value">{{ selectedApplication.nextStep }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes-section">
|
||||
<h4>Notes</h4>
|
||||
<div class="notes-content">
|
||||
<p>{{ selectedApplication.notes }}</p>
|
||||
</div>
|
||||
<div class="add-note">
|
||||
<textarea v-model="newNote" placeholder="Add a new note..."></textarea>
|
||||
<button @click="addNote(selectedApplication)" :disabled="!newNote.trim()">Add Note</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-actions">
|
||||
<button class="update-status">Update Status</button>
|
||||
<button class="withdraw-application" v-if="selectedApplication.status !== 'Rejected'">
|
||||
Withdraw Application
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.applications-container {
|
||||
width: 100%;
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.status-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--card-border);
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.search-applications {
|
||||
input {
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--card-border);
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
width: 250px;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--text-color);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.applications-table-container {
|
||||
overflow-x: auto;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--card-border);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.applications-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th, td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--card-border);
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.rejected {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.job-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
|
||||
&.applied {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
&.screening {
|
||||
background-color: rgba(155, 89, 182, 0.1);
|
||||
color: #9b59b6;
|
||||
}
|
||||
|
||||
&.interview {
|
||||
background-color: rgba(241, 196, 15, 0.1);
|
||||
color: #f1c40f;
|
||||
}
|
||||
|
||||
&.offer {
|
||||
background-color: rgba(46, 204, 113, 0.1);
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
&.rejected {
|
||||
background-color: rgba(231, 76, 60, 0.1);
|
||||
color: #e74c3c;
|
||||
}
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
padding: 0.4rem 0.75rem;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.no-applications {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
// Modal styles
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--card-border);
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 1.5rem;
|
||||
|
||||
.application-details {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
.detail-label {
|
||||
width: 120px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notes-section {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.notes-content {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1rem;
|
||||
white-space: pre-line;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.add-note {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
|
||||
textarea {
|
||||
padding: 0.75rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--card-border);
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
min-height: 80px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: flex-end;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
button {
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.update-status {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.withdraw-application {
|
||||
background-color: transparent;
|
||||
border: 1px solid #e74c3c;
|
||||
color: #e74c3c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Sample dashboard data
|
||||
const stats = ref({
|
||||
applications: 12,
|
||||
interviews: 4,
|
||||
offers: 1,
|
||||
savedJobs: 8
|
||||
});
|
||||
|
||||
const recentActivity = ref([
|
||||
{
|
||||
id: 1,
|
||||
type: 'application',
|
||||
company: 'Tech Innovations Inc.',
|
||||
position: 'Frontend Developer',
|
||||
date: '2025-03-28',
|
||||
status: 'Applied'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'interview',
|
||||
company: 'DataSystems Co.',
|
||||
position: 'Backend Engineer',
|
||||
date: '2025-03-25',
|
||||
status: 'Interview Scheduled'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'saved',
|
||||
company: 'WebSolutions Ltd.',
|
||||
position: 'Full Stack Developer',
|
||||
date: '2025-03-23',
|
||||
status: 'Saved'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
type: 'offer',
|
||||
company: 'Digital Creations',
|
||||
position: 'UI/UX Designer',
|
||||
date: '2025-03-20',
|
||||
status: 'Offer Received'
|
||||
}
|
||||
]);
|
||||
|
||||
const upcomingEvents = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Technical Interview',
|
||||
company: 'DataSystems Co.',
|
||||
date: '2025-04-02',
|
||||
time: '10:00 AM'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Coding Challenge Due',
|
||||
company: 'Tech Innovations Inc.',
|
||||
date: '2025-04-05',
|
||||
time: '11:59 PM'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Follow-up Call',
|
||||
company: 'Digital Creations',
|
||||
date: '2025-04-07',
|
||||
time: '2:30 PM'
|
||||
}
|
||||
]);
|
||||
|
||||
const recommendedJobs = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Senior Vue Developer',
|
||||
company: 'InnovateTech Solutions',
|
||||
location: 'Remote',
|
||||
salary: '$140,000 - $170,000',
|
||||
matchScore: 95
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Frontend Team Lead',
|
||||
company: 'WebWorks Inc.',
|
||||
location: 'San Francisco, CA',
|
||||
salary: '$160,000 - $190,000',
|
||||
matchScore: 88
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Full Stack Engineer',
|
||||
company: 'CloudSystems',
|
||||
location: 'New York, NY',
|
||||
salary: '$130,000 - $160,000',
|
||||
matchScore: 82
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<div class="dashboard-grid">
|
||||
<!-- Stats Cards -->
|
||||
<div class="stats-container">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ stats.applications }}</div>
|
||||
<div class="stat-label">Applications</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ stats.interviews }}</div>
|
||||
<div class="stat-label">Interviews</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ stats.offers }}</div>
|
||||
<div class="stat-label">Offers</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ stats.savedJobs }}</div>
|
||||
<div class="stat-label">Saved Jobs</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="dashboard-card activity-card">
|
||||
<h3 class="card-title">Recent Activity</h3>
|
||||
<div class="activity-list">
|
||||
<div v-for="activity in recentActivity" :key="activity.id" class="activity-item">
|
||||
<div class="activity-icon" :class="activity.type">
|
||||
<span v-if="activity.type === 'application'">📝</span>
|
||||
<span v-else-if="activity.type === 'interview'">🗣️</span>
|
||||
<span v-else-if="activity.type === 'saved'">⭐</span>
|
||||
<span v-else-if="activity.type === 'offer'">🎉</span>
|
||||
</div>
|
||||
<div class="activity-details">
|
||||
<div class="activity-title">{{ activity.position }}</div>
|
||||
<div class="activity-company">{{ activity.company }}</div>
|
||||
<div class="activity-meta">
|
||||
<span class="activity-date">{{ activity.date }}</span>
|
||||
<span class="activity-status" :class="activity.type">{{ activity.status }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Upcoming Events -->
|
||||
<div class="dashboard-card events-card">
|
||||
<h3 class="card-title">Upcoming Events</h3>
|
||||
<div class="events-list">
|
||||
<div v-for="event in upcomingEvents" :key="event.id" class="event-item">
|
||||
<div class="event-date">
|
||||
<div class="event-day">{{ new Date(event.date).getDate() }}</div>
|
||||
<div class="event-month">{{ new Date(event.date).toLocaleString('default', { month: 'short' }) }}</div>
|
||||
</div>
|
||||
<div class="event-details">
|
||||
<div class="event-title">{{ event.title }}</div>
|
||||
<div class="event-company">{{ event.company }}</div>
|
||||
<div class="event-time">{{ event.time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recommended Jobs -->
|
||||
<div class="dashboard-card recommended-card">
|
||||
<h3 class="card-title">Recommended for You</h3>
|
||||
<div class="recommended-list">
|
||||
<div v-for="job in recommendedJobs" :key="job.id" class="recommended-item">
|
||||
<div class="match-score">{{ job.matchScore }}% Match</div>
|
||||
<div class="job-details">
|
||||
<div class="job-title">{{ job.title }}</div>
|
||||
<div class="job-company">{{ job.company }}</div>
|
||||
<div class="job-meta">
|
||||
<span class="job-location">{{ job.location }}</span>
|
||||
<span class="job-salary">{{ job.salary }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="view-job-btn">View</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard {
|
||||
width: 100%;
|
||||
|
||||
.dashboard-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1.5rem;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1rem;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: var(--card-bg, #fff);
|
||||
border: 1px solid var(--card-border, #ddd);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.stat-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background-color: var(--card-bg, #fff);
|
||||
border: 1px solid var(--card-border, #ddd);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.card-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
border-bottom: 1px solid var(--card-border, #ddd);
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-card {
|
||||
.activity-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--card-border, #eee);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
|
||||
&.application {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
}
|
||||
|
||||
&.interview {
|
||||
background-color: rgba(155, 89, 182, 0.1);
|
||||
}
|
||||
|
||||
&.saved {
|
||||
background-color: rgba(241, 196, 15, 0.1);
|
||||
}
|
||||
|
||||
&.offer {
|
||||
background-color: rgba(46, 204, 113, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.activity-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.activity-company {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.activity-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.activity-status {
|
||||
font-weight: 600;
|
||||
|
||||
&.application {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
&.interview {
|
||||
color: #9b59b6;
|
||||
}
|
||||
|
||||
&.saved {
|
||||
color: #f1c40f;
|
||||
}
|
||||
|
||||
&.offer {
|
||||
color: #2ecc71;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.events-card {
|
||||
.events-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.event-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--card-border, #eee);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.event-date {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 8px;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
|
||||
.event-day {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.event-month {
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.event-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.event-company {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.event-time {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.recommended-card {
|
||||
.recommended-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.recommended-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--card-border, #eee);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.match-score {
|
||||
background-color: rgba(46, 204, 113, 0.1);
|
||||
color: #2ecc71;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
margin-right: 1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.job-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.job-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.job-company {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.job-meta {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.view-job-btn {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
padding: 0.4rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
// Props definition using defineProps
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
company: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
location: {
|
||||
type: String,
|
||||
default: 'Remote'
|
||||
},
|
||||
salary: {
|
||||
type: String,
|
||||
default: 'Not specified'
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
applied: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tags: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive state using ref
|
||||
const isApplied = ref(props.applied);
|
||||
|
||||
const applyForJob = () => {
|
||||
isApplied.value = true;
|
||||
// Emit an event that parent components can listen to
|
||||
emit('apply', props.title);
|
||||
};
|
||||
|
||||
// Generate a random pastel color for company logo background
|
||||
const generateRandomColor = () => {
|
||||
// Generate pastel colors by using higher base values
|
||||
const r = Math.floor(Math.random() * 100) + 155;
|
||||
const g = Math.floor(Math.random() * 100) + 155;
|
||||
const b = Math.floor(Math.random() * 100) + 155;
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
};
|
||||
|
||||
// Define emits
|
||||
const emit = defineEmits(['apply', 'click']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="job-card" @click="emit('click')">
|
||||
<div class="job-content">
|
||||
<div class="job-main-info">
|
||||
<div class="company-logo" :style="{ backgroundColor: generateRandomColor() }">
|
||||
{{ company.charAt(0) }}
|
||||
</div>
|
||||
<div class="job-header">
|
||||
<h3 class="job-title">{{ title }}</h3>
|
||||
<div class="job-company">{{ company }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="job-tags" v-if="tags && tags.length > 0">
|
||||
<span v-for="(tag, index) in tags" :key="index" class="job-tag">
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="job-details-row">
|
||||
<div class="job-meta">
|
||||
<div class="meta-item">
|
||||
<span class="meta-icon">📍</span>
|
||||
<span class="meta-text">{{ location }}</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="meta-icon">💰</span>
|
||||
<span class="meta-text">{{ salary }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="job-actions">
|
||||
<button
|
||||
class="action-button apply-button"
|
||||
:class="{ 'applied': isApplied }"
|
||||
@click.stop="applyForJob"
|
||||
:disabled="isApplied"
|
||||
>
|
||||
{{ isApplied ? 'Applied' : 'Apply Now' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.job-card {
|
||||
background-color: var(--card-bg, #fff);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--card-border, #eee);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 1.5rem;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.job-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.job-main-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
.company-logo {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin-right: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.job-header {
|
||||
flex: 1;
|
||||
|
||||
.job-title {
|
||||
margin: 0 0 0.75rem;
|
||||
color: var(--text-color, #333);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.job-company {
|
||||
font-weight: 500;
|
||||
color: var(--text-color-secondary, #555);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.job-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin: 0.75rem 0;
|
||||
|
||||
.job-tag {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.25rem 0.6rem;
|
||||
border-radius: 16px;
|
||||
background-color: var(--tag-bg, rgba(52, 152, 219, 0.1));
|
||||
color: var(--primary-color, #3498db);
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tag-bg-hover, rgba(52, 152, 219, 0.2));
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: rgba(52, 152, 219, 0.2);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.job-details-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.25rem;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.job-meta {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-color-secondary, #777);
|
||||
|
||||
.meta-icon {
|
||||
margin-right: 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.job-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
.action-button {
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border: none;
|
||||
font-size: 0.95rem;
|
||||
|
||||
&.toggle-button {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--primary-color, #3498db);
|
||||
color: var(--primary-color, #3498db);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&.apply-button {
|
||||
background-color: var(--primary-color, #3498db);
|
||||
color: white;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--primary-hover-color, #2980b9);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.applied {
|
||||
background-color: #27ae60;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.job-description-container {
|
||||
padding-top: 1.25rem;
|
||||
border-top: 1px solid var(--card-border, #eee);
|
||||
|
||||
.job-description {
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color, #444);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,526 @@
|
|||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
jobs: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
initiallyExpanded: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['filter-change']);
|
||||
|
||||
// Filter states
|
||||
const searchQuery = ref('');
|
||||
const selectedLocations = ref([]);
|
||||
const selectedSalaryRanges = ref([]);
|
||||
const selectedJobTypes = ref([]);
|
||||
|
||||
// Collapse state
|
||||
const isExpanded = ref(props.initiallyExpanded);
|
||||
|
||||
// Extract unique locations from jobs
|
||||
const availableLocations = computed(() => {
|
||||
const locations = new Set();
|
||||
props.jobs.forEach(job => {
|
||||
if (job.location) {
|
||||
locations.add(job.location);
|
||||
}
|
||||
});
|
||||
return Array.from(locations);
|
||||
});
|
||||
|
||||
// Extract unique job types from jobs
|
||||
const availableJobTypes = computed(() => {
|
||||
const types = new Set();
|
||||
props.jobs.forEach(job => {
|
||||
if (job.employmentType) {
|
||||
types.add(job.employmentType);
|
||||
}
|
||||
});
|
||||
return Array.from(types);
|
||||
});
|
||||
|
||||
// Predefined salary ranges
|
||||
const salaryRanges = [
|
||||
{ id: 1, label: 'Under $100k', min: 0, max: 100000 },
|
||||
{ id: 2, label: '$100k - $130k', min: 100000, max: 130000 },
|
||||
{ id: 3, label: '$130k - $160k', min: 130000, max: 160000 },
|
||||
{ id: 4, label: '$160k+', min: 160000, max: Infinity }
|
||||
];
|
||||
|
||||
// Filter jobs based on all criteria
|
||||
const filterJobs = () => {
|
||||
let filteredJobs = [...props.jobs];
|
||||
|
||||
// Apply search query filter
|
||||
if (searchQuery.value.trim() !== '') {
|
||||
const query = searchQuery.value.toLowerCase();
|
||||
filteredJobs = filteredJobs.filter(job =>
|
||||
job.title.toLowerCase().includes(query) ||
|
||||
job.company.toLowerCase().includes(query) ||
|
||||
job.description.toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
// Apply location filter
|
||||
if (selectedLocations.value.length > 0) {
|
||||
filteredJobs = filteredJobs.filter(job =>
|
||||
selectedLocations.value.includes(job.location)
|
||||
);
|
||||
}
|
||||
|
||||
// Apply job type filter
|
||||
if (selectedJobTypes.value.length > 0) {
|
||||
filteredJobs = filteredJobs.filter(job =>
|
||||
selectedJobTypes.value.includes(job.employmentType)
|
||||
);
|
||||
}
|
||||
|
||||
// Apply salary range filter
|
||||
if (selectedSalaryRanges.value.length > 0) {
|
||||
filteredJobs = filteredJobs.filter(job => {
|
||||
// Extract numeric values from salary string (e.g., "$120,000 - $150,000")
|
||||
const salaryText = job.salary || '';
|
||||
const salaryMatches = salaryText.match(/\$(\d+,?\d*)/g);
|
||||
|
||||
if (!salaryMatches || salaryMatches.length === 0) return false;
|
||||
|
||||
// Convert to numbers
|
||||
const salaryValues = salaryMatches.map(s =>
|
||||
parseInt(s.replace(/[$,]/g, ''), 10)
|
||||
);
|
||||
|
||||
// Get min salary (usually the first number)
|
||||
const minSalary = salaryValues[0];
|
||||
|
||||
// Check if it falls within any of the selected ranges
|
||||
return selectedSalaryRanges.value.some(rangeId => {
|
||||
const range = salaryRanges.find(r => r.id === rangeId);
|
||||
return range && minSalary >= range.min && minSalary <= range.max;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
emit('filter-change', filteredJobs);
|
||||
};
|
||||
|
||||
// Watch for changes in filter criteria
|
||||
watch([searchQuery, selectedLocations, selectedSalaryRanges, selectedJobTypes], () => {
|
||||
filterJobs();
|
||||
}, { deep: true });
|
||||
|
||||
// Clear all filters
|
||||
const clearFilters = () => {
|
||||
searchQuery.value = '';
|
||||
selectedLocations.value = [];
|
||||
selectedSalaryRanges.value = [];
|
||||
selectedJobTypes.value = [];
|
||||
};
|
||||
|
||||
// Toggle location selection
|
||||
const toggleLocation = (location) => {
|
||||
const index = selectedLocations.value.indexOf(location);
|
||||
if (index === -1) {
|
||||
selectedLocations.value.push(location);
|
||||
} else {
|
||||
selectedLocations.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle salary range selection
|
||||
const toggleSalaryRange = (rangeId) => {
|
||||
const index = selectedSalaryRanges.value.indexOf(rangeId);
|
||||
if (index === -1) {
|
||||
selectedSalaryRanges.value.push(rangeId);
|
||||
} else {
|
||||
selectedSalaryRanges.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle job type selection
|
||||
const toggleJobType = (type) => {
|
||||
const index = selectedJobTypes.value.indexOf(type);
|
||||
if (index === -1) {
|
||||
selectedJobTypes.value.push(type);
|
||||
} else {
|
||||
selectedJobTypes.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle filter expansion
|
||||
const toggleFilters = () => {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="job-filters">
|
||||
<div class="search-container">
|
||||
<div class="search-input-wrapper">
|
||||
<span class="search-icon">🔍</span>
|
||||
<input
|
||||
type="text"
|
||||
v-model="searchQuery"
|
||||
placeholder="Search jobs by title, company, or keywords..."
|
||||
class="search-input"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
@click="searchQuery = ''"
|
||||
class="clear-search-btn"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<button @click="toggleFilters" class="toggle-filters-btn">
|
||||
{{ isExpanded ? 'Hide Filters' : 'Show Filters' }}
|
||||
<span class="toggle-icon">{{ isExpanded ? '▲' : '▼' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="filters-container" :class="{ 'collapsed': !isExpanded }">
|
||||
<div class="filter-section">
|
||||
<h3 class="filter-title">Location</h3>
|
||||
<div class="filter-options">
|
||||
<div
|
||||
v-for="location in availableLocations"
|
||||
:key="location"
|
||||
class="filter-option"
|
||||
:class="{ 'selected': selectedLocations.includes(location) }"
|
||||
@click="toggleLocation(location)"
|
||||
>
|
||||
<span class="checkbox">
|
||||
<span v-if="selectedLocations.includes(location)" class="checkbox-inner"></span>
|
||||
</span>
|
||||
<span class="option-label">{{ location }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-section">
|
||||
<h3 class="filter-title">Salary Range</h3>
|
||||
<div class="filter-options">
|
||||
<div
|
||||
v-for="range in salaryRanges"
|
||||
:key="range.id"
|
||||
class="filter-option"
|
||||
:class="{ 'selected': selectedSalaryRanges.includes(range.id) }"
|
||||
@click="toggleSalaryRange(range.id)"
|
||||
>
|
||||
<span class="checkbox">
|
||||
<span v-if="selectedSalaryRanges.includes(range.id)" class="checkbox-inner"></span>
|
||||
</span>
|
||||
<span class="option-label">{{ range.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-section" v-if="availableJobTypes.length > 0">
|
||||
<h3 class="filter-title">Job Type</h3>
|
||||
<div class="filter-options">
|
||||
<div
|
||||
v-for="type in availableJobTypes"
|
||||
:key="type"
|
||||
class="filter-option"
|
||||
:class="{ 'selected': selectedJobTypes.includes(type) }"
|
||||
@click="toggleJobType(type)"
|
||||
>
|
||||
<span class="checkbox">
|
||||
<span v-if="selectedJobTypes.includes(type)" class="checkbox-inner"></span>
|
||||
</span>
|
||||
<span class="option-label">{{ type }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click="clearFilters"
|
||||
class="clear-filters-btn"
|
||||
:disabled="!searchQuery && selectedLocations.length === 0 && selectedSalaryRanges.length === 0 && selectedJobTypes.length === 0"
|
||||
>
|
||||
Clear All Filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.job-filters {
|
||||
background-color: var(--card-bg, #fff);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 2rem;
|
||||
overflow: hidden;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--card-bg, #222);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.search-container {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--card-border, #eee);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-color-tertiary, #999);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 0.9rem 1rem 0.9rem 2.5rem;
|
||||
border: 1px solid var(--card-border, #ddd);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
color: var(--text-color, #333);
|
||||
background-color: var(--input-bg, #fff);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color, #3498db);
|
||||
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--text-color-tertiary, #999);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--bg-color, #2a2a2a);
|
||||
color: var(--text-color, #eee);
|
||||
border-color: var(--card-border, #444);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--text-color-tertiary, #777);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clear-search-btn {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-color-tertiary, #999);
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
padding: 0.25rem;
|
||||
border-radius: 50%;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: var(--text-color, #333);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-filters-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.6rem 1rem;
|
||||
background-color: var(--bg-color, #f8f9fa);
|
||||
border: 1px solid var(--card-border, #ddd);
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-color, #333);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--hover-bg, rgba(0, 0, 0, 0.05));
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--bg-color, #2a2a2a);
|
||||
color: var(--text-color, #eee);
|
||||
border-color: var(--card-border, #444);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--hover-bg, rgba(255, 255, 255, 0.05));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filters-container {
|
||||
padding: 1.5rem;
|
||||
max-height: 1000px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease, padding 0.3s ease, opacity 0.3s ease;
|
||||
opacity: 1;
|
||||
|
||||
&.collapsed {
|
||||
max-height: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
border-top-color: var(--card-border, #444);
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.filter-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 1rem;
|
||||
color: var(--text-color, #333);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
color: var(--text-color, #eee);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
|
||||
.filter-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--hover-bg, rgba(0, 0, 0, 0.05));
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: var(--selected-bg, rgba(52, 152, 219, 0.1));
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
&:hover {
|
||||
background-color: var(--hover-bg, rgba(255, 255, 255, 0.05));
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: var(--selected-bg, rgba(52, 152, 219, 0.2));
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid var(--border-color, #ccc);
|
||||
border-radius: 4px;
|
||||
margin-right: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
|
||||
.checkbox-inner {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: var(--primary-color, #3498db);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected .checkbox {
|
||||
border-color: var(--primary-color, #3498db);
|
||||
}
|
||||
|
||||
.option-label {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-color, #333);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
color: var(--text-color, #eee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clear-filters-btn {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--card-border, #ddd);
|
||||
color: var(--text-color, #333);
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--hover-bg, rgba(0, 0, 0, 0.05));
|
||||
border-color: var(--text-color, #333);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
color: var(--text-color, #eee);
|
||||
border-color: var(--card-border, #444);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--hover-bg, rgba(255, 255, 255, 0.05));
|
||||
border-color: var(--text-color, #eee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.job-filters {
|
||||
.filters-container {
|
||||
.filter-options {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
|
||||
.filter-option {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,400 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
// Sample saved jobs data
|
||||
const savedJobs = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Senior Frontend Developer',
|
||||
company: 'Tech Innovations Inc.',
|
||||
location: 'San Francisco, CA (Remote)',
|
||||
salary: '$140,000 - $170,000',
|
||||
description: 'We are looking for an experienced Frontend Developer proficient in Vue.js to join our growing team. You will be responsible for building user interfaces and implementing new features.',
|
||||
dateAdded: '2025-03-20',
|
||||
tags: ['Vue.js', 'JavaScript', 'CSS', 'Senior']
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Full Stack Engineer',
|
||||
company: 'WebSolutions Ltd.',
|
||||
location: 'New York, NY',
|
||||
salary: '$130,000 - $160,000',
|
||||
description: 'Looking for a versatile developer who can work across the entire stack. Experience with Vue.js and Node.js is a plus.',
|
||||
dateAdded: '2025-03-22',
|
||||
tags: ['Full Stack', 'Node.js', 'Vue.js', 'MongoDB']
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'UI/UX Developer',
|
||||
company: 'Digital Creations',
|
||||
location: 'Remote',
|
||||
salary: '$120,000 - $145,000',
|
||||
description: 'Join our design team to create beautiful and functional user interfaces. Strong design skills and frontend development experience required.',
|
||||
dateAdded: '2025-03-23',
|
||||
tags: ['UI/UX', 'Design', 'Frontend', 'Figma']
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Frontend Team Lead',
|
||||
company: 'InnovateTech Solutions',
|
||||
location: 'Boston, MA (Hybrid)',
|
||||
salary: '$150,000 - $180,000',
|
||||
description: 'Lead a team of frontend developers in building modern web applications. Experience with team management and modern frontend frameworks required.',
|
||||
dateAdded: '2025-03-25',
|
||||
tags: ['Team Lead', 'Management', 'Vue.js', 'React']
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'JavaScript Developer',
|
||||
company: 'CodeCraft Inc.',
|
||||
location: 'Chicago, IL',
|
||||
salary: '$110,000 - $140,000',
|
||||
description: 'Develop and maintain JavaScript applications. Strong knowledge of modern JavaScript frameworks and libraries is essential.',
|
||||
dateAdded: '2025-03-27',
|
||||
tags: ['JavaScript', 'ES6', 'Frontend', 'Backend']
|
||||
}
|
||||
]);
|
||||
|
||||
// Search and filter functionality
|
||||
const searchQuery = ref('');
|
||||
const selectedTags = ref([]);
|
||||
|
||||
// Get all unique tags
|
||||
const allTags = computed(() => {
|
||||
const tags = new Set();
|
||||
savedJobs.value.forEach(job => {
|
||||
job.tags.forEach(tag => tags.add(tag));
|
||||
});
|
||||
return Array.from(tags).sort();
|
||||
});
|
||||
|
||||
// Filter jobs based on search query and selected tags
|
||||
const filteredJobs = computed(() => {
|
||||
return savedJobs.value.filter(job => {
|
||||
// Search query filter
|
||||
const matchesSearch = searchQuery.value === '' ||
|
||||
job.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
job.company.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
job.description.toLowerCase().includes(searchQuery.value.toLowerCase());
|
||||
|
||||
// Tags filter
|
||||
const matchesTags = selectedTags.value.length === 0 ||
|
||||
selectedTags.value.every(tag => job.tags.includes(tag));
|
||||
|
||||
return matchesSearch && matchesTags;
|
||||
});
|
||||
});
|
||||
|
||||
// Toggle tag selection
|
||||
const toggleTag = (tag) => {
|
||||
if (selectedTags.value.includes(tag)) {
|
||||
selectedTags.value = selectedTags.value.filter(t => t !== tag);
|
||||
} else {
|
||||
selectedTags.value.push(tag);
|
||||
}
|
||||
};
|
||||
|
||||
// Remove job from saved
|
||||
const removeJob = (jobId) => {
|
||||
savedJobs.value = savedJobs.value.filter(job => job.id !== jobId);
|
||||
};
|
||||
|
||||
// Apply for job
|
||||
const applyForJob = (jobId) => {
|
||||
console.log(`Applied for job ID: ${jobId}`);
|
||||
// Here you would typically redirect to application form or mark as applied
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="saved-jobs-container">
|
||||
<div class="saved-header">
|
||||
<h2>Saved Jobs ({{ savedJobs.length }})</h2>
|
||||
<div class="search-bar">
|
||||
<input
|
||||
type="text"
|
||||
v-model="searchQuery"
|
||||
placeholder="Search saved jobs..."
|
||||
class="search-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tags-filter">
|
||||
<div class="tags-label">Filter by tags:</div>
|
||||
<div class="tags-list">
|
||||
<button
|
||||
v-for="tag in allTags"
|
||||
:key="tag"
|
||||
@click="toggleTag(tag)"
|
||||
class="tag-button"
|
||||
:class="{ 'active': selectedTags.includes(tag) }"
|
||||
>
|
||||
{{ tag }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="saved-jobs-list" v-if="filteredJobs.length > 0">
|
||||
<div
|
||||
v-for="job in filteredJobs"
|
||||
:key="job.id"
|
||||
class="saved-job-card"
|
||||
>
|
||||
<div class="job-header">
|
||||
<h3 class="job-title">{{ job.title }}</h3>
|
||||
<div class="job-actions">
|
||||
<button class="remove-btn" @click="removeJob(job.id)">
|
||||
<span class="icon">❌</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="job-company">{{ job.company }}</div>
|
||||
<div class="job-details">
|
||||
<div class="job-location">📍 {{ job.location }}</div>
|
||||
<div class="job-salary">💰 {{ job.salary }}</div>
|
||||
</div>
|
||||
|
||||
<div class="job-description">{{ job.description }}</div>
|
||||
|
||||
<div class="job-tags">
|
||||
<span
|
||||
v-for="tag in job.tags"
|
||||
:key="`${job.id}-${tag}`"
|
||||
class="job-tag"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="job-footer">
|
||||
<div class="date-saved">Saved on {{ job.dateAdded }}</div>
|
||||
<button class="apply-btn" @click="applyForJob(job.id)">Apply Now</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="no-jobs-message" v-else>
|
||||
<div class="message-icon">🔍</div>
|
||||
<h3>No saved jobs found</h3>
|
||||
<p>Try adjusting your search or filters to see more results.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.saved-jobs-container {
|
||||
width: 100%;
|
||||
|
||||
.saved-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
.search-input {
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--card-border);
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
width: 300px;
|
||||
font-size: 0.9rem;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--text-color);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-filter {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
.tags-label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tags-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
|
||||
.tag-button {
|
||||
padding: 0.4rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
background-color: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
color: var(--text-color);
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.saved-jobs-list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.saved-job-card {
|
||||
background-color: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.job-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.job-title {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.job-actions {
|
||||
.remove-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.job-company {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.job-details {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.job-description {
|
||||
margin-bottom: 1.25rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.job-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.25rem;
|
||||
|
||||
.job-tag {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
color: var(--primary-color);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.job-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.date-saved {
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.apply-btn {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.6rem 1.25rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-hover-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-jobs-message {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
background-color: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 8px;
|
||||
|
||||
.message-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
<script setup>
|
||||
import { ref, inject } from 'vue';
|
||||
|
||||
// Navigation state
|
||||
const isCollapsed = ref(false);
|
||||
|
||||
// Get theme state from parent
|
||||
const isDarkMode = inject('isDarkMode');
|
||||
const parentToggleTheme = inject('toggleTheme');
|
||||
|
||||
// Get current active section from parent
|
||||
const activeSection = inject('activeSection');
|
||||
|
||||
const toggleNav = () => {
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
};
|
||||
|
||||
const toggleTheme = () => {
|
||||
// Call the parent's toggle function with the new value
|
||||
parentToggleTheme(!isDarkMode.value);
|
||||
};
|
||||
|
||||
defineEmits(['navigate']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="side-nav" :class="{ 'collapsed': isCollapsed }">
|
||||
<div class="nav-header">
|
||||
<h2 class="nav-title">Jobs</h2>
|
||||
<button class="toggle-btn" @click="toggleNav">
|
||||
{{ isCollapsed ? '→' : '←' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="theme-toggle" :class="{ 'collapsed-mode': isCollapsed }">
|
||||
<button @click="toggleTheme" class="theme-btn">
|
||||
<span class="icon">{{ isDarkMode ? '☀️' : '🌙' }}</span>
|
||||
<span class="text" v-if="!isCollapsed">{{ isDarkMode ? 'Light Mode' : 'Dark Mode' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('navigate', 'dashboard')" :class="{ 'active': activeSection === 'dashboard' }">
|
||||
<span class="icon">📊</span>
|
||||
<span class="text" v-if="!isCollapsed">Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('navigate', 'jobs')" :class="{ 'active': activeSection === 'jobs' }">
|
||||
<span class="icon">💼</span>
|
||||
<span class="text" v-if="!isCollapsed">Jobs</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('navigate', 'applications')" :class="{ 'active': activeSection === 'applications' }">
|
||||
<span class="icon">📝</span>
|
||||
<span class="text" v-if="!isCollapsed">Applications</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('navigate', 'saved')" :class="{ 'active': activeSection === 'saved' }">
|
||||
<span class="icon">⭐</span>
|
||||
<span class="text" v-if="!isCollapsed">Saved</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('navigate', 'profile')" :class="{ 'active': activeSection === 'profile' }">
|
||||
<span class="icon">👤</span>
|
||||
<span class="text" v-if="!isCollapsed">Profile</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('navigate', 'settings')" :class="{ 'active': activeSection === 'settings' }">
|
||||
<span class="icon">⚙️</span>
|
||||
<span class="text" v-if="!isCollapsed">Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.side-nav {
|
||||
width: 240px;
|
||||
height: 100vh;
|
||||
background-color: var(--sidebar-bg);
|
||||
border-right: 1px solid var(--sidebar-border);
|
||||
transition: width 0.3s ease;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.collapsed {
|
||||
width: 60px;
|
||||
|
||||
.nav-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.nav-title {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.2rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
color: var(--text-color);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid var(--sidebar-border);
|
||||
|
||||
&.collapsed-mode {
|
||||
.text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-btn {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
background: none;
|
||||
border: 1px solid var(--sidebar-border);
|
||||
border-radius: 4px;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 0.75rem;
|
||||
font-size: 1.2rem;
|
||||
width: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
padding: 1rem 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s;
|
||||
border-radius: 0 4px 4px 0;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
color: var(--primary-color);
|
||||
border-left: 3px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 0.75rem;
|
||||
font-size: 1.2rem;
|
||||
width: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { createApp } from 'vue'
|
||||
import './assets/scss/main.scss'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|
||||
|
|
@ -0,0 +1,659 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/helper-string-parser@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||
|
||||
"@babel/parser@^7.25.3":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec"
|
||||
integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.27.0"
|
||||
|
||||
"@babel/types@^7.27.0":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559"
|
||||
integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
|
||||
"@esbuild/aix-ppc64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz#c33cf6bbee34975626b01b80451cbb72b4c6c91d"
|
||||
integrity sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==
|
||||
|
||||
"@esbuild/android-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz#ea766015c7d2655164f22100d33d7f0308a28d6d"
|
||||
integrity sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==
|
||||
|
||||
"@esbuild/android-arm@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.1.tgz#e84d2bf2fe2e6177a0facda3a575b2139fd3cb9c"
|
||||
integrity sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==
|
||||
|
||||
"@esbuild/android-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.1.tgz#58337bee3bc6d78d10425e5500bd11370cfdfbed"
|
||||
integrity sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==
|
||||
|
||||
"@esbuild/darwin-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz#a46805c1c585d451aa83be72500bd6e8495dd591"
|
||||
integrity sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==
|
||||
|
||||
"@esbuild/darwin-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz#0643e003bb238c63fc93ddbee7d26a003be3cd98"
|
||||
integrity sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==
|
||||
|
||||
"@esbuild/freebsd-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz#cff18da5469c09986b93e87979de5d6872fe8f8e"
|
||||
integrity sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==
|
||||
|
||||
"@esbuild/freebsd-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz#362fc09c2de14987621c1878af19203c46365dde"
|
||||
integrity sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==
|
||||
|
||||
"@esbuild/linux-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz#aa90d5b02efc97a271e124e6d1cea490634f7498"
|
||||
integrity sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==
|
||||
|
||||
"@esbuild/linux-arm@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz#dfcefcbac60a20918b19569b4b657844d39db35a"
|
||||
integrity sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==
|
||||
|
||||
"@esbuild/linux-ia32@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz#6f9527077ccb7953ed2af02e013d4bac69f13754"
|
||||
integrity sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==
|
||||
|
||||
"@esbuild/linux-loong64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz#287d2412a5456e5860c2839d42a4b51284d1697c"
|
||||
integrity sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==
|
||||
|
||||
"@esbuild/linux-mips64el@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz#530574b9e1bc5d20f7a4f44c5f045e26f3783d57"
|
||||
integrity sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==
|
||||
|
||||
"@esbuild/linux-ppc64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz#5d7e6b283a0b321ea42c6bc0abeb9eb99c1f5589"
|
||||
integrity sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==
|
||||
|
||||
"@esbuild/linux-riscv64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz#14fa0cd073c26b4ee2465d18cd1e18eea7859fa8"
|
||||
integrity sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==
|
||||
|
||||
"@esbuild/linux-s390x@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz#e677b4b9d1b384098752266ccaa0d52a420dc1aa"
|
||||
integrity sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==
|
||||
|
||||
"@esbuild/linux-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz#f1c796b78fff5ce393658313e8c58613198d9954"
|
||||
integrity sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==
|
||||
|
||||
"@esbuild/netbsd-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz#0d280b7dfe3973f111b02d5fe9f3063b92796d29"
|
||||
integrity sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==
|
||||
|
||||
"@esbuild/netbsd-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz#be663893931a4bb3f3a009c5cc24fa9681cc71c0"
|
||||
integrity sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==
|
||||
|
||||
"@esbuild/openbsd-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz#d9021b884233673a05dc1cc26de0bf325d824217"
|
||||
integrity sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==
|
||||
|
||||
"@esbuild/openbsd-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz#9f1dc1786ed2e2938c404b06bcc48be9a13250de"
|
||||
integrity sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==
|
||||
|
||||
"@esbuild/sunos-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz#89aac24a4b4115959b3f790192cf130396696c27"
|
||||
integrity sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==
|
||||
|
||||
"@esbuild/win32-arm64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz#354358647a6ea98ea6d243bf48bdd7a434999582"
|
||||
integrity sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==
|
||||
|
||||
"@esbuild/win32-ia32@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz#8cea7340f2647eba951a041dc95651e3908cd4cb"
|
||||
integrity sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==
|
||||
|
||||
"@esbuild/win32-x64@0.25.1":
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz#7d79922cb2d88f9048f06393dbf62d2e4accb584"
|
||||
integrity sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||
|
||||
"@parcel/watcher-android-arm64@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1"
|
||||
integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==
|
||||
|
||||
"@parcel/watcher-darwin-arm64@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67"
|
||||
integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
|
||||
|
||||
"@parcel/watcher-darwin-x64@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8"
|
||||
integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==
|
||||
|
||||
"@parcel/watcher-freebsd-x64@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b"
|
||||
integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==
|
||||
|
||||
"@parcel/watcher-linux-arm-glibc@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1"
|
||||
integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==
|
||||
|
||||
"@parcel/watcher-linux-arm-musl@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e"
|
||||
integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==
|
||||
|
||||
"@parcel/watcher-linux-arm64-glibc@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30"
|
||||
integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==
|
||||
|
||||
"@parcel/watcher-linux-arm64-musl@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2"
|
||||
integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==
|
||||
|
||||
"@parcel/watcher-linux-x64-glibc@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e"
|
||||
integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==
|
||||
|
||||
"@parcel/watcher-linux-x64-musl@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee"
|
||||
integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==
|
||||
|
||||
"@parcel/watcher-win32-arm64@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243"
|
||||
integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==
|
||||
|
||||
"@parcel/watcher-win32-ia32@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6"
|
||||
integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==
|
||||
|
||||
"@parcel/watcher-win32-x64@2.5.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947"
|
||||
integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
|
||||
|
||||
"@parcel/watcher@^2.4.1":
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200"
|
||||
integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
is-glob "^4.0.3"
|
||||
micromatch "^4.0.5"
|
||||
node-addon-api "^7.0.0"
|
||||
optionalDependencies:
|
||||
"@parcel/watcher-android-arm64" "2.5.1"
|
||||
"@parcel/watcher-darwin-arm64" "2.5.1"
|
||||
"@parcel/watcher-darwin-x64" "2.5.1"
|
||||
"@parcel/watcher-freebsd-x64" "2.5.1"
|
||||
"@parcel/watcher-linux-arm-glibc" "2.5.1"
|
||||
"@parcel/watcher-linux-arm-musl" "2.5.1"
|
||||
"@parcel/watcher-linux-arm64-glibc" "2.5.1"
|
||||
"@parcel/watcher-linux-arm64-musl" "2.5.1"
|
||||
"@parcel/watcher-linux-x64-glibc" "2.5.1"
|
||||
"@parcel/watcher-linux-x64-musl" "2.5.1"
|
||||
"@parcel/watcher-win32-arm64" "2.5.1"
|
||||
"@parcel/watcher-win32-ia32" "2.5.1"
|
||||
"@parcel/watcher-win32-x64" "2.5.1"
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.38.0.tgz#6ba67cc0f3a2d7e3a208256a349c2cb2798f57be"
|
||||
integrity sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.38.0.tgz#c8806f88fd6727d3cf144c4ffb00f40d451b6618"
|
||||
integrity sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.38.0.tgz#c4654989b97bba0de7205cf5b3342984d4451d5a"
|
||||
integrity sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.38.0.tgz#77ee357aeeefe3fe8bee33df18c240e391450476"
|
||||
integrity sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==
|
||||
|
||||
"@rollup/rollup-freebsd-arm64@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.38.0.tgz#ac8028c99221d1cef22788adda465077d5926911"
|
||||
integrity sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==
|
||||
|
||||
"@rollup/rollup-freebsd-x64@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.38.0.tgz#64376ff0e1541cd8677e74898782ec4935277e02"
|
||||
integrity sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.38.0.tgz#7de1584c09adcac08f90d1e500c679c428b6eb36"
|
||||
integrity sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.38.0.tgz#1d5d0f28d93cdc37d60c381c6bbe649bed4960d5"
|
||||
integrity sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.38.0.tgz#a97f73a43a374e44bef4a9ed84899c26454831ea"
|
||||
integrity sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.38.0.tgz#00e3b646a7976752052ebc72d005808b9e7f2801"
|
||||
integrity sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.38.0.tgz#0d6dcaa3671cf987faace4b34ab7320ee3c18b65"
|
||||
integrity sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.38.0.tgz#54c2d7a4d86767001475f0157c408fd042f7fd8f"
|
||||
integrity sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.38.0.tgz#8cb565417b29851a0c549614898bdab689f23187"
|
||||
integrity sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.38.0.tgz#8bc00b75fd07b15c35a54b41a5f052c01dbf925b"
|
||||
integrity sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.38.0.tgz#1fe4a88b97e36d64dbf1f01cfa7842d269a094cf"
|
||||
integrity sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.38.0.tgz#11c32c463e68a86e279cda090a9405a7558f9406"
|
||||
integrity sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.38.0.tgz#520c2a8547672ec6c56a6833f6d38e9380d63dc7"
|
||||
integrity sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.38.0.tgz#d27ab565009357014c9f2d6393ee58bd63a63cb8"
|
||||
integrity sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.38.0.tgz#6c02847c60fcc7a6d74e00a60f350d079558d84d"
|
||||
integrity sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.38.0":
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.38.0.tgz#38197da22c1af7b6b5d1cc7541757379193b8e83"
|
||||
integrity sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==
|
||||
|
||||
"@types/estree@1.0.7":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8"
|
||||
integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==
|
||||
|
||||
"@vitejs/plugin-vue@^5.2.1":
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz#71a8fc82d4d2e425af304c35bf389506f674d89b"
|
||||
integrity sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==
|
||||
|
||||
"@vue/compiler-core@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz#b0ae6c4347f60c03e849a05d34e5bf747c9bda05"
|
||||
integrity sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.25.3"
|
||||
"@vue/shared" "3.5.13"
|
||||
entities "^4.5.0"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-dom@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz#bb1b8758dbc542b3658dda973b98a1c9311a8a58"
|
||||
integrity sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
|
||||
"@vue/compiler-sfc@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz#461f8bd343b5c06fac4189c4fef8af32dea82b46"
|
||||
integrity sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.25.3"
|
||||
"@vue/compiler-core" "3.5.13"
|
||||
"@vue/compiler-dom" "3.5.13"
|
||||
"@vue/compiler-ssr" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.11"
|
||||
postcss "^8.4.48"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-ssr@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz#e771adcca6d3d000f91a4277c972a996d07f43ba"
|
||||
integrity sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
|
||||
"@vue/reactivity@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.13.tgz#b41ff2bb865e093899a22219f5b25f97b6fe155f"
|
||||
integrity sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==
|
||||
dependencies:
|
||||
"@vue/shared" "3.5.13"
|
||||
|
||||
"@vue/runtime-core@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz#1fafa4bf0b97af0ebdd9dbfe98cd630da363a455"
|
||||
integrity sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
|
||||
"@vue/runtime-dom@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz#610fc795de9246300e8ae8865930d534e1246215"
|
||||
integrity sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.5.13"
|
||||
"@vue/runtime-core" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
csstype "^3.1.3"
|
||||
|
||||
"@vue/server-renderer@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz#429ead62ee51de789646c22efe908e489aad46f7"
|
||||
integrity sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==
|
||||
dependencies:
|
||||
"@vue/compiler-ssr" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
|
||||
"@vue/shared@3.5.13":
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f"
|
||||
integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==
|
||||
|
||||
braces@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
|
||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
chokidar@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30"
|
||||
integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
|
||||
dependencies:
|
||||
readdirp "^4.0.1"
|
||||
|
||||
csstype@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
|
||||
detect-libc@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
||||
|
||||
entities@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
esbuild@^0.25.0:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.1.tgz#a16b8d070b6ad4871935277bda6ccfe852e3fa2f"
|
||||
integrity sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==
|
||||
optionalDependencies:
|
||||
"@esbuild/aix-ppc64" "0.25.1"
|
||||
"@esbuild/android-arm" "0.25.1"
|
||||
"@esbuild/android-arm64" "0.25.1"
|
||||
"@esbuild/android-x64" "0.25.1"
|
||||
"@esbuild/darwin-arm64" "0.25.1"
|
||||
"@esbuild/darwin-x64" "0.25.1"
|
||||
"@esbuild/freebsd-arm64" "0.25.1"
|
||||
"@esbuild/freebsd-x64" "0.25.1"
|
||||
"@esbuild/linux-arm" "0.25.1"
|
||||
"@esbuild/linux-arm64" "0.25.1"
|
||||
"@esbuild/linux-ia32" "0.25.1"
|
||||
"@esbuild/linux-loong64" "0.25.1"
|
||||
"@esbuild/linux-mips64el" "0.25.1"
|
||||
"@esbuild/linux-ppc64" "0.25.1"
|
||||
"@esbuild/linux-riscv64" "0.25.1"
|
||||
"@esbuild/linux-s390x" "0.25.1"
|
||||
"@esbuild/linux-x64" "0.25.1"
|
||||
"@esbuild/netbsd-arm64" "0.25.1"
|
||||
"@esbuild/netbsd-x64" "0.25.1"
|
||||
"@esbuild/openbsd-arm64" "0.25.1"
|
||||
"@esbuild/openbsd-x64" "0.25.1"
|
||||
"@esbuild/sunos-x64" "0.25.1"
|
||||
"@esbuild/win32-arm64" "0.25.1"
|
||||
"@esbuild/win32-ia32" "0.25.1"
|
||||
"@esbuild/win32-x64" "0.25.1"
|
||||
|
||||
estree-walker@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||
|
||||
fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
immutable@^5.0.2:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.1.tgz#d4cb552686f34b076b3dcf23c4384c04424d8354"
|
||||
integrity sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||
|
||||
is-glob@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
|
||||
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
magic-string@^0.30.11:
|
||||
version "0.30.17"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
||||
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||
|
||||
micromatch@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||
dependencies:
|
||||
braces "^3.0.3"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
nanoid@^3.3.8:
|
||||
version "3.3.11"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
|
||||
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
|
||||
|
||||
node-addon-api@^7.0.0:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||
|
||||
picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
postcss@^8.4.48, postcss@^8.5.3:
|
||||
version "8.5.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb"
|
||||
integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==
|
||||
dependencies:
|
||||
nanoid "^3.3.8"
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
readdirp@^4.0.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d"
|
||||
integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
|
||||
|
||||
rollup@^4.30.1:
|
||||
version "4.38.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.38.0.tgz#e1ef4939aef0e1295f750ae4f73d1c8b7fc3abb8"
|
||||
integrity sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==
|
||||
dependencies:
|
||||
"@types/estree" "1.0.7"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-android-arm-eabi" "4.38.0"
|
||||
"@rollup/rollup-android-arm64" "4.38.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.38.0"
|
||||
"@rollup/rollup-darwin-x64" "4.38.0"
|
||||
"@rollup/rollup-freebsd-arm64" "4.38.0"
|
||||
"@rollup/rollup-freebsd-x64" "4.38.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.38.0"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.38.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.38.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.38.0"
|
||||
"@rollup/rollup-linux-loongarch64-gnu" "4.38.0"
|
||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.38.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.38.0"
|
||||
"@rollup/rollup-linux-riscv64-musl" "4.38.0"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.38.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.38.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.38.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.38.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.38.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.38.0"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
sass@^1.86.0:
|
||||
version "1.86.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.86.0.tgz#f49464fb6237a903a93f4e8760ef6e37a5030114"
|
||||
integrity sha512-zV8vGUld/+mP4KbMLJMX7TyGCuUp7hnkOScgCMsWuHtns8CWBoz+vmEhoGMXsaJrbUP8gj+F1dLvVe79sK8UdA==
|
||||
dependencies:
|
||||
chokidar "^4.0.0"
|
||||
immutable "^5.0.2"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
optionalDependencies:
|
||||
"@parcel/watcher" "^2.4.1"
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
vite@^6.2.0:
|
||||
version "6.2.3"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.3.tgz#249e92d32886981ab46bc1f049ac72abc6fa81e2"
|
||||
integrity sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==
|
||||
dependencies:
|
||||
esbuild "^0.25.0"
|
||||
postcss "^8.5.3"
|
||||
rollup "^4.30.1"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vue@^3.5.13:
|
||||
version "3.5.13"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.13.tgz#9f760a1a982b09c0c04a867903fc339c9f29ec0a"
|
||||
integrity sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.5.13"
|
||||
"@vue/compiler-sfc" "3.5.13"
|
||||
"@vue/runtime-dom" "3.5.13"
|
||||
"@vue/server-renderer" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
Loading…
Reference in New Issue