| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- //
- // JobBadges.swift
- // indeed_ai_job_search
- //
- import AppKit
- import SwiftUI
- struct RemoteWorkBadge: View {
- let type: RemoteType
- var body: some View {
- Label(type.displayName, systemImage: icon)
- .font(AppTypography.meta)
- .foregroundStyle(AppColors.textSecondary)
- .labelStyle(.titleAndIcon)
- }
- private var icon: String {
- switch type {
- case .remote: return "house"
- case .hybrid: return "building.2"
- case .onsite: return "mappin.and.ellipse"
- case .unknown: return "questionmark.circle"
- }
- }
- }
- struct SalaryLabel: View {
- let salary: SalaryRange?
- var body: some View {
- if let text = salary?.formatted {
- Text(text)
- .font(AppTypography.salary)
- .foregroundStyle(AppColors.salary)
- }
- }
- }
- struct RelativeDateLabel: View {
- let date: Date
- var body: some View {
- Text(Self.relativeString(from: date))
- .font(AppTypography.meta)
- .foregroundStyle(AppColors.textTertiary)
- }
- static func relativeString(from date: Date) -> String {
- let seconds = max(0, Int(Date().timeIntervalSince(date)))
- let days = seconds / 86_400
- let hours = seconds / 3_600
- if days <= 0 {
- if hours <= 0 { return "Just now" }
- if hours == 1 { return "1 hour ago" }
- return "\(hours) hours ago"
- }
- if days == 1 { return "1 day ago" }
- if days < 7 { return "\(days) days ago" }
- let weeks = days / 7
- if weeks == 1 { return "1 week ago" }
- return "\(weeks) weeks ago"
- }
- }
- struct EasyApplyBadge: View {
- var body: some View {
- Label("Easy Apply", systemImage: "bolt.fill")
- .font(AppTypography.caption)
- .foregroundStyle(AppColors.easyApply)
- .padding(.horizontal, 8)
- .padding(.vertical, 4)
- .background(AppColors.brandSoft)
- .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
- }
- }
- struct FeaturedBadge: View {
- var body: some View {
- Text("Featured")
- .font(AppTypography.caption)
- .foregroundStyle(AppColors.featuredText)
- .padding(.horizontal, 8)
- .padding(.vertical, 4)
- .background(AppColors.featured)
- .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
- }
- }
- struct UrgentlyHiringBadge: View {
- var body: some View {
- Text("Urgently hiring")
- .font(AppTypography.caption)
- .foregroundStyle(AppColors.urgentText)
- .padding(.horizontal, 8)
- .padding(.vertical, 4)
- .background(AppColors.urgent)
- .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
- }
- }
- struct UnavailableBadge: View {
- var body: some View {
- Text("Unavailable")
- .font(AppTypography.caption)
- .foregroundStyle(AppColors.unavailable)
- .padding(.horizontal, 8)
- .padding(.vertical, 4)
- .background(AppColors.border)
- .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
- }
- }
- struct CompanyLogoView: View {
- let listing: JobListing
- var body: some View {
- ZStack {
- RoundedRectangle(cornerRadius: 10, style: .continuous)
- .fill(logoBackground)
- Image(systemName: listing.logoSymbol)
- .font(.system(size: 18, weight: .semibold))
- .foregroundStyle(logoForeground)
- }
- .frame(width: 44, height: 44)
- .accessibilityHidden(true)
- }
- private var logoBackground: Color {
- switch listing.company.lowercased() {
- case "apple": return Color.black.opacity(0.08)
- case "microsoft": return Color.blue.opacity(0.12)
- default: return AppColors.brandSoft
- }
- }
- private var logoForeground: Color {
- switch listing.company.lowercased() {
- case "apple": return AppColors.textPrimary
- case "microsoft": return Color.blue
- default: return AppColors.brand
- }
- }
- }
- struct SaveJobButton: View {
- let isSaved: Bool
- let action: () -> Void
- var body: some View {
- Button(action: action) {
- Image(systemName: isSaved ? "bookmark.fill" : "bookmark")
- .font(.system(size: 14, weight: .medium))
- .foregroundStyle(isSaved ? AppColors.brand : AppColors.textTertiary)
- .frame(width: 28, height: 28)
- .contentShape(Rectangle())
- }
- .buttonStyle(.plain)
- .help(isSaved ? "Remove from saved jobs" : "Save job")
- .accessibilityLabel(isSaved ? "Unsave job" : "Save job")
- }
- }
- struct OpenIndeedLinkButton: View {
- let listing: JobListing
- var body: some View {
- Button {
- JobListingOpener.open(listing)
- } label: {
- Label("Open on Indeed", systemImage: "arrow.up.right.square")
- .font(AppTypography.caption)
- .foregroundStyle(AppColors.brand)
- }
- .buttonStyle(.plain)
- .help("Open listing on Indeed")
- .accessibilityLabel("Open on Indeed")
- }
- }
|