Mass UI Updates
This commit is contained in:
@@ -0,0 +1,853 @@
|
|||||||
|
================================================================================
|
||||||
|
LIQUID GLASS DESIGN SYSTEM PLAYBOOK
|
||||||
|
Apple-Inspired Glassmorphism
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
OVERVIEW
|
||||||
|
--------
|
||||||
|
This playbook documents the liquid glass (glassmorphism) design system used
|
||||||
|
throughout the Job-Pages application. This design creates a premium, Apple-like
|
||||||
|
aesthetic using frosted glass effects, subtle transparency, and smooth transitions.
|
||||||
|
|
||||||
|
Last Updated: 2024
|
||||||
|
Version: 2.0
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
CORE PRINCIPLES
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
1. TRANSPARENCY & BLUR
|
||||||
|
- Use backdrop-blur for frosted glass effects
|
||||||
|
- Semi-transparent backgrounds (white/40, white/60, white/70, white/80)
|
||||||
|
- Subtle borders with white/20 to white/40 opacity
|
||||||
|
- Gradient background provides depth: from-gray-50 via-blue-50/30 to-indigo-50/40
|
||||||
|
|
||||||
|
2. DEPTH & SHADOWS
|
||||||
|
- Layered shadows (shadow-sm, shadow-lg, shadow-xl, shadow-2xl)
|
||||||
|
- Colored shadows for interactive elements (shadow-indigo-500/30, shadow-indigo-500/50)
|
||||||
|
- Hover states increase shadow intensity
|
||||||
|
- Shadow-lg for buttons, shadow-xl for cards, shadow-2xl for modals/dropdowns
|
||||||
|
|
||||||
|
3. SMOOTH TRANSITIONS
|
||||||
|
- All interactive elements use transition-all or transition-colors
|
||||||
|
- Scale effects on hover (hover:scale-105 for avatars, hover:scale-[1.02] for cards)
|
||||||
|
- Opacity changes for hover states
|
||||||
|
- Duration-300 for smooth animations
|
||||||
|
|
||||||
|
4. ROUNDED CORNERS
|
||||||
|
- Standard: rounded-xl (12px) - buttons, inputs
|
||||||
|
- Large cards: rounded-2xl (16px) - main containers, cards
|
||||||
|
- Modals: rounded-3xl (24px) - modal content
|
||||||
|
- Full rounded: rounded-full (for avatars)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
BACKGROUND SYSTEM
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
MAIN PAGE BACKGROUND
|
||||||
|
--------------------
|
||||||
|
Class: bg-gradient-to-br from-gray-50 via-blue-50/30 to-indigo-50/40
|
||||||
|
Purpose: Creates a subtle gradient that provides depth and visual interest
|
||||||
|
Usage: Applied to <body> element on all pages
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<body class="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50/30 to-indigo-50/40">
|
||||||
|
|
||||||
|
This gradient creates the foundation for the glassmorphism effect, allowing
|
||||||
|
transparent elements to show through with a beautiful backdrop.
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
NAVIGATION BAR
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
STANDARD NAVIGATION
|
||||||
|
-------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/70
|
||||||
|
- border-b border-white/20
|
||||||
|
- shadow-sm
|
||||||
|
- sticky top-0 z-40
|
||||||
|
|
||||||
|
Purpose: Creates a frosted glass navigation that stays at the top
|
||||||
|
|
||||||
|
Container:
|
||||||
|
- max-w-7xl mx-auto
|
||||||
|
- px-4 sm:px-6 lg:px-8
|
||||||
|
- py-4 sm:py-4 (consistent padding on all screen sizes)
|
||||||
|
- px-2 (additional horizontal padding)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<nav class="backdrop-blur-xl bg-white/70 border-b border-white/20 shadow-sm sticky top-0 z-40">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="flex flex-col sm:flex-row justify-between items-center h-16 sm:h-auto py-4 sm:py-4 gap-4 sm:gap-0 px-2">
|
||||||
|
<!-- Navigation content -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
NAVIGATION LINKS
|
||||||
|
----------------
|
||||||
|
Classes:
|
||||||
|
- text-indigo-600
|
||||||
|
- hover:text-indigo-700
|
||||||
|
- font-medium
|
||||||
|
- text-sm sm:text-base
|
||||||
|
- transition-colors
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<a href="dashboard.html" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base transition-colors">← Dashboard</a>
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
CARDS & CONTAINERS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
STANDARD CARD
|
||||||
|
-------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-xl
|
||||||
|
- border border-white/30
|
||||||
|
- p-6
|
||||||
|
|
||||||
|
Purpose: Main content cards with glassmorphism effect
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
|
|
||||||
|
CARD WITH HOVER EFFECT
|
||||||
|
----------------------
|
||||||
|
Additional classes:
|
||||||
|
- hover:bg-white/70
|
||||||
|
- hover:shadow-2xl
|
||||||
|
- hover:border-white/50
|
||||||
|
- hover:scale-[1.02]
|
||||||
|
- transition-all duration-300
|
||||||
|
- cursor-pointer
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6
|
||||||
|
hover:bg-white/70 hover:shadow-2xl hover:border-white/50 hover:scale-[1.02]
|
||||||
|
transition-all duration-300 cursor-pointer">
|
||||||
|
|
||||||
|
SEARCH/FILTER CONTAINER
|
||||||
|
-----------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-xl
|
||||||
|
- border border-white/30
|
||||||
|
- p-6
|
||||||
|
- mb-6
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 mb-6">
|
||||||
|
|
||||||
|
MODAL/CONTAINER
|
||||||
|
---------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-2xl
|
||||||
|
- bg-white/80
|
||||||
|
- rounded-3xl
|
||||||
|
- shadow-2xl
|
||||||
|
- border border-white/30
|
||||||
|
|
||||||
|
Backdrop:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-black/20
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="fixed inset-0 backdrop-blur-sm bg-black/20 z-50">
|
||||||
|
<div class="backdrop-blur-2xl bg-white/80 rounded-3xl shadow-2xl border border-white/30">
|
||||||
|
<!-- Modal content -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
QUICK INFO CARD (Smaller card)
|
||||||
|
-------------------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/40
|
||||||
|
- rounded-2xl
|
||||||
|
- p-4
|
||||||
|
- border border-white/30
|
||||||
|
- shadow-lg
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="backdrop-blur-xl bg-white/40 rounded-2xl p-4 border border-white/30 shadow-lg">
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
BUTTONS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
PRIMARY BUTTON (ACTIVE STATE)
|
||||||
|
------------------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-indigo-600/90
|
||||||
|
- text-white
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-indigo-700/90
|
||||||
|
- hover:shadow-xl
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<button class="backdrop-blur-sm bg-indigo-600/90 text-white rounded-xl shadow-lg
|
||||||
|
hover:bg-indigo-700/90 hover:shadow-xl transition-all">
|
||||||
|
|
||||||
|
SECONDARY BUTTON (INACTIVE STATE)
|
||||||
|
----------------------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- text-gray-700
|
||||||
|
- rounded-xl
|
||||||
|
- hover:bg-white/40
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<button class="backdrop-blur-sm text-gray-700 rounded-xl hover:bg-white/40 transition-all">
|
||||||
|
|
||||||
|
BUTTON GROUP CONTAINER (View Toggle)
|
||||||
|
-------------------------------------
|
||||||
|
Classes:
|
||||||
|
- inline-flex
|
||||||
|
- rounded-2xl
|
||||||
|
- border border-white/30
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- p-1
|
||||||
|
- shadow-lg
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="inline-flex rounded-2xl border border-white/30 backdrop-blur-xl bg-white/60 p-1 shadow-lg">
|
||||||
|
<!-- Toggle buttons inside -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
VIEW TOGGLE BUTTONS
|
||||||
|
-------------------
|
||||||
|
Active Button:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-indigo-600/90
|
||||||
|
- text-white
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-indigo-700/90
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
Inactive Button:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- text-gray-700
|
||||||
|
- rounded-xl
|
||||||
|
- hover:bg-white/40
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
FORM BUTTONS
|
||||||
|
------------
|
||||||
|
Save Button:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-indigo-600/90
|
||||||
|
- text-white
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-indigo-700/90
|
||||||
|
- hover:shadow-xl
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
Cancel Button:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-white/60
|
||||||
|
- border border-white/40
|
||||||
|
- text-gray-700
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-white/80
|
||||||
|
- hover:shadow-xl
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
MAP BUTTONS (Apple Maps / Google Maps)
|
||||||
|
---------------------------------------
|
||||||
|
Apple Maps:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-gray-900/90
|
||||||
|
- text-white
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-gray-800/90
|
||||||
|
- hover:shadow-xl
|
||||||
|
|
||||||
|
Google Maps:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-blue-600/90
|
||||||
|
- text-white
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-blue-700/90
|
||||||
|
- hover:shadow-xl
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
FORM ELEMENTS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
INPUT FIELDS
|
||||||
|
------------
|
||||||
|
Classes:
|
||||||
|
- border border-white/40
|
||||||
|
- rounded-xl
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-white/40
|
||||||
|
- focus:ring-2
|
||||||
|
- focus:ring-indigo-500/50
|
||||||
|
- focus:border-indigo-300/50
|
||||||
|
- transition-all
|
||||||
|
- shadow-sm
|
||||||
|
- px-3 py-2 (or px-4 py-3 for larger inputs)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<input class="w-full px-3 py-2 border border-white/40 rounded-xl backdrop-blur-sm bg-white/40
|
||||||
|
focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-300/50
|
||||||
|
transition-all shadow-sm">
|
||||||
|
|
||||||
|
SEARCH INPUT
|
||||||
|
------------
|
||||||
|
Classes:
|
||||||
|
- Same as input fields
|
||||||
|
- Larger padding: px-4 py-3
|
||||||
|
- text-base
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<input class="w-full px-4 py-3 border border-white/40 rounded-xl backdrop-blur-sm bg-white/40
|
||||||
|
focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-300/50
|
||||||
|
outline-none text-base transition-all shadow-sm">
|
||||||
|
|
||||||
|
SELECT DROPDOWNS
|
||||||
|
----------------
|
||||||
|
Same classes as input fields
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<select class="w-full px-3 py-2 border border-white/40 rounded-xl backdrop-blur-sm bg-white/40
|
||||||
|
focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-300/50
|
||||||
|
transition-all shadow-sm">
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
AVATARS & ICONS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
AVATAR BUTTON
|
||||||
|
-------------
|
||||||
|
Classes:
|
||||||
|
- rounded-full
|
||||||
|
- bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500
|
||||||
|
- w-10 h-10
|
||||||
|
- shadow-lg shadow-indigo-500/30
|
||||||
|
- hover:scale-105
|
||||||
|
- hover:shadow-indigo-500/50
|
||||||
|
- transition-all duration-300
|
||||||
|
- flex items-center justify-center
|
||||||
|
- text-white
|
||||||
|
|
||||||
|
Purpose: User avatar with gradient background and smooth hover effect
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<button class="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500
|
||||||
|
shadow-lg shadow-indigo-500/30 hover:scale-105 hover:shadow-indigo-500/50
|
||||||
|
transition-all duration-300 flex items-center justify-center text-white">
|
||||||
|
<span class="text-xs font-semibold">IC</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
AVATAR INITIALS
|
||||||
|
---------------
|
||||||
|
Classes:
|
||||||
|
- text-xs
|
||||||
|
- font-semibold
|
||||||
|
- text-white
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
DROPDOWNS & MENUS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
DROPDOWN MENU
|
||||||
|
-------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/80
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-2xl
|
||||||
|
- border border-white/30
|
||||||
|
- py-2
|
||||||
|
- overflow-hidden
|
||||||
|
- absolute right-0 mt-2
|
||||||
|
- z-50
|
||||||
|
- w-48
|
||||||
|
|
||||||
|
Purpose: User dropdown menu with glassmorphism
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="hidden absolute right-0 mt-2 w-48 backdrop-blur-xl bg-white/80 rounded-2xl
|
||||||
|
shadow-2xl border border-white/30 py-2 z-50 overflow-hidden">
|
||||||
|
|
||||||
|
DROPDOWN HEADER
|
||||||
|
---------------
|
||||||
|
Classes:
|
||||||
|
- px-4 py-3
|
||||||
|
- border-b border-white/20
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="px-4 py-3 border-b border-white/20">
|
||||||
|
<p class="text-xs text-gray-500 font-medium">Signed in as</p>
|
||||||
|
<p class="text-sm font-semibold text-gray-900 truncate mt-0.5">user@example.com</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
DROPDOWN ITEM
|
||||||
|
-------------
|
||||||
|
Classes:
|
||||||
|
- px-4 py-2.5
|
||||||
|
- text-sm
|
||||||
|
- text-gray-700
|
||||||
|
- hover:bg-white/40
|
||||||
|
- transition-colors
|
||||||
|
- font-medium
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<a class="block px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 transition-colors font-medium">
|
||||||
|
Preferences
|
||||||
|
</a>
|
||||||
|
|
||||||
|
DESTRUCTIVE DROPDOWN ITEM
|
||||||
|
--------------------------
|
||||||
|
Classes:
|
||||||
|
- w-full text-left
|
||||||
|
- px-4 py-2.5
|
||||||
|
- text-sm
|
||||||
|
- text-red-600
|
||||||
|
- hover:bg-red-50/50
|
||||||
|
- transition-colors
|
||||||
|
- font-medium
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<button class="w-full text-left px-4 py-2.5 text-sm text-red-600 hover:bg-red-50/50
|
||||||
|
transition-colors font-medium">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
TABLES
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
TABLE CONTAINER
|
||||||
|
---------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-xl
|
||||||
|
- border border-white/30
|
||||||
|
- overflow-hidden
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 overflow-hidden">
|
||||||
|
|
||||||
|
TABLE HEADER
|
||||||
|
------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-white/40
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<thead class="backdrop-blur-sm bg-white/40">
|
||||||
|
|
||||||
|
TABLE BODY
|
||||||
|
----------
|
||||||
|
Classes:
|
||||||
|
- bg-white/30
|
||||||
|
- divide-y divide-white/20
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<tbody class="bg-white/30 divide-y divide-white/20">
|
||||||
|
|
||||||
|
TABLE CELLS
|
||||||
|
-----------
|
||||||
|
Standard cells:
|
||||||
|
- px-3 sm:px-6
|
||||||
|
- py-4
|
||||||
|
- text-sm
|
||||||
|
- text-gray-900
|
||||||
|
|
||||||
|
Header cells:
|
||||||
|
- px-3 sm:px-6
|
||||||
|
- py-2 sm:py-3
|
||||||
|
- text-xs
|
||||||
|
- font-medium
|
||||||
|
- text-gray-500
|
||||||
|
- uppercase
|
||||||
|
- tracking-wider
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
MODALS & OVERLAYS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
MODAL BACKDROP
|
||||||
|
--------------
|
||||||
|
Classes:
|
||||||
|
- fixed inset-0
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-black/20
|
||||||
|
- overflow-y-auto
|
||||||
|
- z-50
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="fixed inset-0 backdrop-blur-sm bg-black/20 overflow-y-auto z-50">
|
||||||
|
|
||||||
|
MODAL CONTENT
|
||||||
|
-------------
|
||||||
|
Classes:
|
||||||
|
- relative top-0 sm:top-10
|
||||||
|
- mx-auto
|
||||||
|
- p-4 sm:p-6
|
||||||
|
- w-full sm:max-w-7xl (or sm:max-w-4xl for smaller modals)
|
||||||
|
- backdrop-blur-2xl
|
||||||
|
- bg-white/80
|
||||||
|
- rounded-none sm:rounded-3xl
|
||||||
|
- shadow-2xl
|
||||||
|
- border border-white/30
|
||||||
|
- mb-0 sm:mb-10
|
||||||
|
- min-h-screen sm:min-h-[600px]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="relative top-0 sm:top-10 mx-auto p-4 sm:p-6 w-full sm:max-w-7xl
|
||||||
|
backdrop-blur-2xl bg-white/80 rounded-none sm:rounded-3xl shadow-2xl
|
||||||
|
border border-white/30 mb-0 sm:mb-10 min-h-screen sm:min-h-[600px]">
|
||||||
|
|
||||||
|
MODAL CARDS (Inside modal)
|
||||||
|
---------------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-xl
|
||||||
|
- border border-white/30
|
||||||
|
- p-6
|
||||||
|
- mb-6
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 mb-6">
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
PREFERENCES PAGE
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
PREFERENCES CARD
|
||||||
|
----------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-xl
|
||||||
|
- border border-white/30
|
||||||
|
- p-6
|
||||||
|
|
||||||
|
PREFERENCE SECTION DIVIDER
|
||||||
|
---------------------------
|
||||||
|
Classes:
|
||||||
|
- border-b border-white/20
|
||||||
|
- py-4
|
||||||
|
|
||||||
|
RADIO BUTTON OPTIONS
|
||||||
|
--------------------
|
||||||
|
Container:
|
||||||
|
- flex flex-col sm:flex-row
|
||||||
|
- gap-4
|
||||||
|
|
||||||
|
Option Label:
|
||||||
|
- flex items-center
|
||||||
|
- p-4
|
||||||
|
- border-2 border-white/30
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-white/40
|
||||||
|
- rounded-2xl
|
||||||
|
- cursor-pointer
|
||||||
|
- hover:border-indigo-500/50
|
||||||
|
- hover:bg-white/60
|
||||||
|
- transition-all
|
||||||
|
- shadow-sm
|
||||||
|
|
||||||
|
Selected State (via JavaScript):
|
||||||
|
- border-indigo-500
|
||||||
|
- bg-indigo-50/50
|
||||||
|
- shadow-md
|
||||||
|
|
||||||
|
Example:
|
||||||
|
<label class="view-option flex items-center p-4 border-2 border-white/30 backdrop-blur-sm
|
||||||
|
bg-white/40 rounded-2xl cursor-pointer hover:border-indigo-500/50
|
||||||
|
hover:bg-white/60 transition-all shadow-sm" data-value="table">
|
||||||
|
<input type="radio" name="defaultView" value="table" class="sr-only">
|
||||||
|
<!-- Content -->
|
||||||
|
</label>
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
OPACITY LEVELS REFERENCE
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
BACKGROUND OPACITIES:
|
||||||
|
- /20 = Very subtle borders and dividers (border-white/20)
|
||||||
|
- /30 = Standard borders and dividers (border-white/30)
|
||||||
|
- /40 = Input backgrounds, table rows, option backgrounds (bg-white/40)
|
||||||
|
- /50 = Hover states, focus rings, selected states (hover:bg-white/50, bg-indigo-50/50)
|
||||||
|
- /60 = Standard card backgrounds (bg-white/60)
|
||||||
|
- /70 = Navigation bar (bg-white/70)
|
||||||
|
- /80 = Dropdowns, modals (bg-white/80)
|
||||||
|
- /90 = Button backgrounds active state (bg-indigo-600/90, bg-gray-900/90)
|
||||||
|
|
||||||
|
BLUR LEVELS:
|
||||||
|
- backdrop-blur-sm = Subtle blur (4px) - inputs, buttons, table headers
|
||||||
|
- backdrop-blur-xl = Standard blur (24px) - cards, navigation, dropdowns
|
||||||
|
- backdrop-blur-2xl = Strong blur (40px) - modals, overlays
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
SHADOW LEVELS REFERENCE
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
- shadow-sm = Small shadow (1px) - subtle elevation
|
||||||
|
- shadow-lg = Large shadow (10px) - buttons, active states, containers
|
||||||
|
- shadow-xl = Extra large (20px) - cards, main containers
|
||||||
|
- shadow-2xl = 2X extra large (25px) - modals, dropdowns
|
||||||
|
|
||||||
|
COLORED SHADOWS:
|
||||||
|
- shadow-indigo-500/30 = Subtle colored shadow (avatar default)
|
||||||
|
- shadow-indigo-500/50 = More prominent colored shadow (avatar hover)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
TRANSITION PATTERNS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
STANDARD TRANSITION
|
||||||
|
-------------------
|
||||||
|
Classes: transition-all duration-300
|
||||||
|
|
||||||
|
SMOOTH HOVER
|
||||||
|
------------
|
||||||
|
Classes: transition-all duration-300 hover:scale-105
|
||||||
|
|
||||||
|
BUTTON TRANSITION
|
||||||
|
-----------------
|
||||||
|
Classes: transition-all (includes color, shadow, scale changes)
|
||||||
|
|
||||||
|
COLOR TRANSITION
|
||||||
|
----------------
|
||||||
|
Classes: transition-colors (for hover state color changes only)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
COLOR PALETTE
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
PRIMARY COLORS:
|
||||||
|
- indigo-600/90 = Primary button background
|
||||||
|
- indigo-700/90 = Primary button hover
|
||||||
|
- indigo-500/30 = Shadow color (subtle)
|
||||||
|
- indigo-500/50 = Focus ring, stronger shadow, selected states
|
||||||
|
- indigo-300/50 = Focus border color
|
||||||
|
|
||||||
|
GRADIENT COLORS (Avatar):
|
||||||
|
- from-indigo-500 = Start of gradient
|
||||||
|
- via-purple-500 = Middle of gradient
|
||||||
|
- to-pink-500 = End of gradient
|
||||||
|
|
||||||
|
TEXT COLORS:
|
||||||
|
- text-gray-500 = Secondary text, labels
|
||||||
|
- text-gray-600 = Body text, descriptions
|
||||||
|
- text-gray-700 = Standard text (inactive buttons, dropdown items)
|
||||||
|
- text-gray-900 = Headings, important text, dropdown headers
|
||||||
|
- text-white = Text on colored backgrounds
|
||||||
|
- text-indigo-600 = Links, active states
|
||||||
|
- text-indigo-700 = Link hover state
|
||||||
|
- text-red-600 = Destructive actions (logout)
|
||||||
|
|
||||||
|
BORDER COLORS:
|
||||||
|
- border-white/20 = Subtle borders (navigation, dividers)
|
||||||
|
- border-white/30 = Standard borders (cards, containers)
|
||||||
|
- border-white/40 = Input borders
|
||||||
|
- border-indigo-300/50 = Focus borders
|
||||||
|
- border-indigo-500 = Selected state borders
|
||||||
|
|
||||||
|
BACKGROUND GRADIENT:
|
||||||
|
- from-gray-50 = Light gray start
|
||||||
|
- via-blue-50/30 = Light blue middle (30% opacity)
|
||||||
|
- to-indigo-50/40 = Light indigo end (40% opacity)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
COMMON PATTERNS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
SEARCH/FILTER CONTAINER
|
||||||
|
-----------------------
|
||||||
|
Classes:
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- rounded-2xl
|
||||||
|
- shadow-xl
|
||||||
|
- border border-white/30
|
||||||
|
- p-6
|
||||||
|
- mb-6
|
||||||
|
|
||||||
|
VIEW TOGGLE BUTTONS
|
||||||
|
-------------------
|
||||||
|
Container:
|
||||||
|
- inline-flex
|
||||||
|
- rounded-2xl
|
||||||
|
- border border-white/30
|
||||||
|
- backdrop-blur-xl
|
||||||
|
- bg-white/60
|
||||||
|
- p-1
|
||||||
|
- shadow-lg
|
||||||
|
|
||||||
|
Active Button:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- bg-indigo-600/90
|
||||||
|
- text-white
|
||||||
|
- rounded-xl
|
||||||
|
- shadow-lg
|
||||||
|
- hover:bg-indigo-700/90
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
Inactive Button:
|
||||||
|
- backdrop-blur-sm
|
||||||
|
- text-gray-700
|
||||||
|
- rounded-xl
|
||||||
|
- hover:bg-white/40
|
||||||
|
- transition-all
|
||||||
|
|
||||||
|
MESSAGE CONTAINER
|
||||||
|
-----------------
|
||||||
|
Success Message:
|
||||||
|
- bg-green-50
|
||||||
|
- border-green-200
|
||||||
|
- text-green-700
|
||||||
|
- border
|
||||||
|
- px-4 py-3
|
||||||
|
- rounded-xl
|
||||||
|
- text-sm
|
||||||
|
- backdrop-blur-sm
|
||||||
|
|
||||||
|
Error Message:
|
||||||
|
- bg-red-50
|
||||||
|
- border-red-200
|
||||||
|
- text-red-700
|
||||||
|
- border
|
||||||
|
- px-4 py-3
|
||||||
|
- rounded-xl
|
||||||
|
- text-sm
|
||||||
|
- backdrop-blur-sm
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
RESPONSIVE DESIGN
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
BREAKPOINTS:
|
||||||
|
- sm: 640px
|
||||||
|
- md: 768px
|
||||||
|
- lg: 1024px
|
||||||
|
|
||||||
|
MOBILE (< 640px):
|
||||||
|
- Single column layouts
|
||||||
|
- Full-width buttons
|
||||||
|
- Stacked navigation elements
|
||||||
|
- Full-screen modals (rounded-none)
|
||||||
|
- Smaller text sizes (text-sm)
|
||||||
|
- Reduced padding
|
||||||
|
|
||||||
|
TABLET (640px - 1024px):
|
||||||
|
- Two-column grids where applicable
|
||||||
|
- Horizontal navigation
|
||||||
|
- Constrained modals (rounded-3xl)
|
||||||
|
- Medium text sizes
|
||||||
|
|
||||||
|
DESKTOP (> 1024px):
|
||||||
|
- Multi-column layouts
|
||||||
|
- Max-width containers (max-w-7xl)
|
||||||
|
- Side-by-side elements
|
||||||
|
- Larger text and spacing
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
IMPLEMENTATION CHECKLIST
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
When creating a new component with liquid glass style:
|
||||||
|
|
||||||
|
[ ] 1. Apply appropriate backdrop-blur level (sm, xl, or 2xl)
|
||||||
|
[ ] 2. Set semi-transparent background (white/40 to white/80 based on context)
|
||||||
|
[ ] 3. Add rounded corners (rounded-xl for buttons/inputs, rounded-2xl for cards)
|
||||||
|
[ ] 4. Include shadow (shadow-lg for buttons, shadow-xl for cards, shadow-2xl for modals)
|
||||||
|
[ ] 5. Add subtle border (border-white/20 to border-white/40)
|
||||||
|
[ ] 6. Add transition-all or transition-colors for smooth interactions
|
||||||
|
[ ] 7. Include hover states with increased opacity/shadow/scale
|
||||||
|
[ ] 8. Test on gradient background to ensure glass effect is visible
|
||||||
|
[ ] 9. Ensure proper z-index layering (z-40 for nav, z-50 for modals/dropdowns)
|
||||||
|
[ ] 10. Verify responsive behavior on mobile, tablet, and desktop
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
ACCESSIBILITY NOTES
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
- Ensure sufficient contrast for text readability
|
||||||
|
- Focus states should be clearly visible (focus:ring-2 focus:ring-indigo-500/50)
|
||||||
|
- Interactive elements should have clear hover feedback
|
||||||
|
- Maintain WCAG AA contrast ratios (4.5:1 for normal text)
|
||||||
|
- Use semantic HTML elements
|
||||||
|
- Provide keyboard navigation support
|
||||||
|
- Screen reader friendly labels and ARIA attributes where needed
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
BROWSER COMPATIBILITY
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
backdrop-blur requires:
|
||||||
|
- Chrome 76+
|
||||||
|
- Firefox 103+
|
||||||
|
- Safari 9+
|
||||||
|
- Edge 79+
|
||||||
|
|
||||||
|
For older browsers, consider:
|
||||||
|
- Fallback solid backgrounds
|
||||||
|
- CSS filters as alternative
|
||||||
|
- Progressive enhancement approach
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
PERFORMANCE CONSIDERATIONS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
- backdrop-blur can be performance-intensive
|
||||||
|
- Use sparingly on mobile devices
|
||||||
|
- Consider will-change: transform for animated elements
|
||||||
|
- Test on lower-end devices
|
||||||
|
- Limit the number of blurred layers
|
||||||
|
- Use backdrop-blur-sm for smaller elements when possible
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
CURRENT IMPLEMENTATION STATUS
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
PAGES IMPLEMENTED:
|
||||||
|
✓ Dashboard (dashboard.html)
|
||||||
|
✓ Jobs Page (jobs.html)
|
||||||
|
✓ Notes Page (notes.html)
|
||||||
|
✓ Preferences Page (preferences.html)
|
||||||
|
✓ Login Page (index.html) - standard design, not glassmorphism
|
||||||
|
|
||||||
|
COMPONENTS IMPLEMENTED:
|
||||||
|
✓ Navigation bar
|
||||||
|
✓ User avatar with dropdown
|
||||||
|
✓ Cards and containers
|
||||||
|
✓ Search/filter sections
|
||||||
|
✓ View toggle buttons
|
||||||
|
✓ Tables
|
||||||
|
✓ Modals
|
||||||
|
✓ Form inputs and selects
|
||||||
|
✓ Buttons (all states)
|
||||||
|
✓ Preferences page with radio options
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
END OF PLAYBOOK
|
||||||
|
================================================================================
|
||||||
File diff suppressed because it is too large
Load Diff
+72
-18
@@ -7,23 +7,45 @@
|
|||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pocketbase@latest/dist/pocketbase.umd.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pocketbase@latest/dist/pocketbase.umd.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-gray-50">
|
<body class="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50/30 to-indigo-50/40">
|
||||||
<!-- Navigation Header -->
|
<!-- Navigation Header -->
|
||||||
<nav class="bg-white shadow-sm border-b border-gray-200">
|
<nav class="backdrop-blur-xl bg-white/70 border-b border-white/20 shadow-sm sticky top-0 z-40">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex flex-col sm:flex-row justify-between items-center h-16 sm:h-auto py-4 sm:py-0 gap-4 sm:gap-0">
|
<div class="flex flex-row justify-end items-center py-4 px-2">
|
||||||
<div class="text-lg sm:text-xl font-bold text-gray-800">Job Tracking</div>
|
<div class="flex items-center gap-3 relative">
|
||||||
<div class="flex flex-col sm:flex-row items-center gap-3 w-full sm:w-auto">
|
<div class="relative">
|
||||||
<div class="text-xs sm:text-sm text-gray-600" id="userEmail"></div>
|
<button
|
||||||
|
onclick="toggleUserDropdown()"
|
||||||
|
class="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex items-center justify-center text-white font-medium text-sm hover:scale-105 focus:outline-none transition-all duration-300 shadow-lg shadow-indigo-500/30 hover:shadow-indigo-500/50"
|
||||||
|
id="userAvatarBtn"
|
||||||
|
>
|
||||||
|
<span id="userInitials" class="text-xs font-semibold"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="userDropdown"
|
||||||
|
class="hidden absolute right-0 mt-2 w-48 backdrop-blur-xl bg-white/80 rounded-2xl shadow-2xl border border-white/30 py-2 z-50 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="px-4 py-3 border-b border-white/20">
|
||||||
|
<p class="text-xs text-gray-500 font-medium">Signed in as</p>
|
||||||
|
<p class="text-sm font-semibold text-gray-900 truncate mt-0.5" id="userEmail"></p>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="preferences"
|
||||||
|
class="block px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Preferences
|
||||||
|
</a>
|
||||||
<button
|
<button
|
||||||
onclick="logout()"
|
onclick="logout()"
|
||||||
class="w-full sm:w-auto bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition"
|
class="w-full text-left px-4 py-2.5 text-sm text-red-600 hover:bg-red-50/50 focus:outline-none transition-colors font-medium"
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@@ -37,7 +59,7 @@
|
|||||||
<!-- Dashboard Cards -->
|
<!-- Dashboard Cards -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||||
<!-- Job Tracking Card -->
|
<!-- Job Tracking Card -->
|
||||||
<div class="bg-white rounded-lg shadow border border-gray-200 p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 hover:bg-white/70 transition-all duration-300">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center">
|
<div class="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center">
|
||||||
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -47,11 +69,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3 class="text-lg font-semibold text-gray-800 mb-2">Job Tracking</h3>
|
<h3 class="text-lg font-semibold text-gray-800 mb-2">Job Tracking</h3>
|
||||||
<p class="text-sm text-gray-600 mb-4">View and manage job information</p>
|
<p class="text-sm text-gray-600 mb-4">View and manage job information</p>
|
||||||
<a href="jobs.html" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm">View Jobs →</a>
|
<a href="jobs" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm">View Jobs →</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Reports Card -->
|
<!-- Reports Card -->
|
||||||
<div class="bg-white rounded-lg shadow border border-gray-200 p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 hover:bg-white/70 transition-all duration-300">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center">
|
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center">
|
||||||
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -61,12 +83,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3 class="text-lg font-semibold text-gray-800 mb-2">Notes & Reports</h3>
|
<h3 class="text-lg font-semibold text-gray-800 mb-2">Notes & Reports</h3>
|
||||||
<p class="text-sm text-gray-600 mb-4">Manage job notes and reports</p>
|
<p class="text-sm text-gray-600 mb-4">Manage job notes and reports</p>
|
||||||
<a href="notes.html" class="text-green-600 hover:text-green-700 font-medium text-sm">View Notes →</a>
|
<a href="notes" class="text-green-600 hover:text-green-700 font-medium text-sm">View Notes →</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Quick Stats -->
|
<!-- Quick Stats -->
|
||||||
<div class="bg-white rounded-lg shadow border border-gray-200 p-4 sm:p-6 mb-6 sm:mb-8">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-4 sm:p-6 mb-6 sm:mb-8">
|
||||||
<h2 class="text-base sm:text-lg font-semibold text-gray-800 mb-4">Quick Stats</h2>
|
<h2 class="text-base sm:text-lg font-semibold text-gray-800 mb-4">Quick Stats</h2>
|
||||||
<div class="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-3 sm:gap-4">
|
<div class="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-3 sm:gap-4">
|
||||||
<!-- Total Jobs -->
|
<!-- Total Jobs -->
|
||||||
@@ -176,11 +198,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Quick Actions -->
|
<!-- Quick Actions -->
|
||||||
<div class="bg-white rounded-lg shadow border border-gray-200 p-4 sm:p-6 mb-6 sm:mb-8">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-4 sm:p-6 mb-6 sm:mb-8">
|
||||||
<h2 class="text-base sm:text-lg font-semibold text-gray-800 mb-4">Quick Actions</h2>
|
<h2 class="text-base sm:text-lg font-semibold text-gray-800 mb-4">Quick Actions</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<!-- Add New Job -->
|
<!-- Add New Job -->
|
||||||
<a href="jobs.html" class="flex items-center p-4 border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-indigo-300 transition-colors">
|
<a href="jobs" class="flex items-center p-4 border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-indigo-300 transition-colors">
|
||||||
<div class="w-10 h-10 bg-indigo-100 rounded-lg flex items-center justify-center mr-4 flex-shrink-0">
|
<div class="w-10 h-10 bg-indigo-100 rounded-lg flex items-center justify-center mr-4 flex-shrink-0">
|
||||||
<svg class="w-5 h-5 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||||
@@ -193,7 +215,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- View All Jobs -->
|
<!-- View All Jobs -->
|
||||||
<a href="jobs.html" class="flex items-center p-4 border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-green-300 transition-colors">
|
<a href="jobs" class="flex items-center p-4 border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-green-300 transition-colors">
|
||||||
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mr-4 flex-shrink-0">
|
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mr-4 flex-shrink-0">
|
||||||
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
||||||
@@ -211,11 +233,15 @@
|
|||||||
<script src="js/pocketbase.js"></script>
|
<script src="js/pocketbase.js"></script>
|
||||||
<script src="js/config.js"></script>
|
<script src="js/config.js"></script>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/preferences.js"></script>
|
||||||
<script src="js/jobs.js"></script>
|
<script src="js/jobs.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Check authentication
|
// Check authentication
|
||||||
checkAuth();
|
checkAuth();
|
||||||
|
|
||||||
|
// Initialize dark mode
|
||||||
|
initializeDarkMode();
|
||||||
|
|
||||||
// Load dashboard data
|
// Load dashboard data
|
||||||
async function loadDashboard() {
|
async function loadDashboard() {
|
||||||
try {
|
try {
|
||||||
@@ -256,13 +282,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set user email
|
// Set user email and initials
|
||||||
const user = pb.authStore.model;
|
const user = pb.authStore.model;
|
||||||
if (user) {
|
if (user) {
|
||||||
document.getElementById('userEmail').textContent = user.email || '';
|
const email = user.email || '';
|
||||||
document.getElementById('welcomeTitle').textContent = `Welcome, ${user.email?.split('@')[0] || 'User'}`;
|
document.getElementById('userEmail').textContent = email;
|
||||||
|
document.getElementById('welcomeTitle').textContent = `Welcome, ${email.split('@')[0] || 'User'}`;
|
||||||
|
|
||||||
|
// Set initials for avatar
|
||||||
|
const initials = email
|
||||||
|
.split('@')[0]
|
||||||
|
.split('.')
|
||||||
|
.map(part => part.charAt(0).toUpperCase())
|
||||||
|
.join('')
|
||||||
|
.substring(0, 2) || 'U';
|
||||||
|
document.getElementById('userInitials').textContent = initials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toggle user dropdown
|
||||||
|
function toggleUserDropdown() {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
dropdown.classList.toggle('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
const button = document.getElementById('userAvatarBtn');
|
||||||
|
if (dropdown && !dropdown.contains(e.target) && !button.contains(e.target)) {
|
||||||
|
dropdown.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make function globally accessible
|
||||||
|
window.toggleUserDropdown = toggleUserDropdown;
|
||||||
|
|
||||||
loadDashboard();
|
loadDashboard();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+3
-3
@@ -24,7 +24,7 @@
|
|||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none transition text-base"
|
class="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition text-base"
|
||||||
placeholder="Enter your email"
|
placeholder="Enter your email"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none transition text-base"
|
class="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition text-base"
|
||||||
placeholder="Enter your password"
|
placeholder="Enter your password"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await login(email, password);
|
await login(email, password);
|
||||||
window.location.href = 'dashboard.html';
|
window.location.href = 'dashboard';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage.textContent = error.message || 'Login failed. Please try again.';
|
errorMessage.textContent = error.message || 'Login failed. Please try again.';
|
||||||
errorMessage.classList.remove('hidden');
|
errorMessage.classList.remove('hidden');
|
||||||
|
|||||||
@@ -8,27 +8,66 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/pocketbase@latest/dist/pocketbase.umd.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pocketbase@latest/dist/pocketbase.umd.js"></script>
|
||||||
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||||
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Custom dropdown scrollbar styling */
|
||||||
|
[id$="_menu"]::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
[id$="_menu"]::-webkit-scrollbar-track {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
[id$="_menu"]::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(79, 70, 229, 0.3);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
[id$="_menu"]::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(79, 70, 229, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-gray-50">
|
<body class="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50/30 to-indigo-50/40">
|
||||||
<!-- Navigation Header -->
|
<!-- Navigation Header -->
|
||||||
<nav class="bg-white shadow-sm border-b border-gray-200">
|
<nav class="backdrop-blur-xl bg-white/70 border-b border-white/20 shadow-sm sticky top-0 z-40">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex flex-col sm:flex-row justify-between items-center h-16 sm:h-auto py-4 sm:py-0 gap-4 sm:gap-0">
|
<div class="flex flex-row justify-between items-center py-4 px-2">
|
||||||
<div class="flex items-center gap-4 w-full sm:w-auto">
|
<div class="flex items-center gap-4">
|
||||||
<a href="dashboard.html" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base">← Dashboard</a>
|
<a href="dashboard" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base transition-colors">← Dashboard</a>
|
||||||
<div class="text-lg sm:text-xl font-bold text-gray-800">Job Tracking</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row items-center gap-3 w-full sm:w-auto">
|
<div class="flex items-center gap-3 relative">
|
||||||
<div class="text-xs sm:text-sm text-gray-600" id="userEmail"></div>
|
<div class="relative">
|
||||||
|
<button
|
||||||
|
onclick="toggleUserDropdown()"
|
||||||
|
class="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex items-center justify-center text-white font-medium text-sm hover:scale-105 focus:outline-none transition-all duration-300 shadow-lg shadow-indigo-500/30 hover:shadow-indigo-500/50"
|
||||||
|
id="userAvatarBtn"
|
||||||
|
>
|
||||||
|
<span id="userInitials" class="text-xs font-semibold"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="userDropdown"
|
||||||
|
class="hidden absolute right-0 mt-2 w-48 backdrop-blur-xl bg-white/80 rounded-2xl shadow-2xl border border-white/30 py-2 z-50 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="px-4 py-3 border-b border-white/20">
|
||||||
|
<p class="text-xs text-gray-500 font-medium">Signed in as</p>
|
||||||
|
<p class="text-sm font-semibold text-gray-900 truncate mt-0.5" id="userEmail"></p>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="preferences"
|
||||||
|
class="block px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Preferences
|
||||||
|
</a>
|
||||||
<button
|
<button
|
||||||
onclick="logout()"
|
onclick="logout()"
|
||||||
class="w-full sm:w-auto bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition"
|
class="w-full text-left px-4 py-2.5 text-sm text-red-600 hover:bg-red-50/50 focus:outline-none transition-colors font-medium"
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@@ -37,14 +76,14 @@
|
|||||||
<div id="messageContainer" class="mb-4"></div>
|
<div id="messageContainer" class="mb-4"></div>
|
||||||
|
|
||||||
<!-- Search and Filter Section -->
|
<!-- Search and Filter Section -->
|
||||||
<div class="bg-white rounded-lg shadow border border-gray-200 p-4 mb-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 mb-6">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<!-- Search Bar -->
|
<!-- Search Bar -->
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="searchInput"
|
id="searchInput"
|
||||||
placeholder="Search by job number, name, or client..."
|
placeholder="Search by job number, name, or client..."
|
||||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base"
|
class="w-full px-4 py-3 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- Filter Section -->
|
<!-- Filter Section -->
|
||||||
@@ -82,11 +121,11 @@
|
|||||||
|
|
||||||
<!-- View Toggle -->
|
<!-- View Toggle -->
|
||||||
<div class="mb-4 flex justify-end">
|
<div class="mb-4 flex justify-end">
|
||||||
<div class="inline-flex rounded-lg border border-gray-300 bg-white p-1">
|
<div class="inline-flex rounded-2xl border border-white/30 backdrop-blur-xl bg-white/60 p-1 shadow-lg">
|
||||||
<button
|
<button
|
||||||
onclick="switchView('table')"
|
onclick="switchView('table')"
|
||||||
id="tableViewBtn"
|
id="tableViewBtn"
|
||||||
class="px-4 py-2 rounded-md text-sm font-medium transition focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-indigo-600 text-white"
|
class="px-4 py-2 rounded-xl text-sm font-medium transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500/50 backdrop-blur-sm bg-indigo-600/90 text-white shadow-lg hover:bg-indigo-700/90"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||||||
@@ -96,7 +135,7 @@
|
|||||||
<button
|
<button
|
||||||
onclick="switchView('card')"
|
onclick="switchView('card')"
|
||||||
id="cardViewBtn"
|
id="cardViewBtn"
|
||||||
class="px-4 py-2 rounded-md text-sm font-medium transition focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-700 hover:bg-gray-50"
|
class="px-4 py-2 rounded-xl text-sm font-medium transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500/50 text-gray-700 hover:bg-white/40 backdrop-blur-sm"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path>
|
||||||
@@ -107,10 +146,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Jobs Table View -->
|
<!-- Jobs Table View -->
|
||||||
<div id="tableView" class="bg-white rounded-lg shadow border border-gray-200 overflow-hidden">
|
<div id="tableView" class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 overflow-hidden">
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="min-w-full divide-y divide-gray-200">
|
<table class="min-w-full divide-y divide-gray-200">
|
||||||
<thead class="bg-gray-50">
|
<thead class="backdrop-blur-sm bg-white/40">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Job Number</th>
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Job Number</th>
|
||||||
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Job Name</th>
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Job Name</th>
|
||||||
@@ -122,7 +161,7 @@
|
|||||||
<th class="px-3 sm:px-6 py-2 sm:py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
<th class="px-3 sm:px-6 py-2 sm:py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="jobsTableBody" class="bg-white divide-y divide-gray-200">
|
<tbody id="jobsTableBody" class="bg-white/30 divide-y divide-white/20">
|
||||||
<tr id="loadingRow">
|
<tr id="loadingRow">
|
||||||
<td colspan="8" class="px-6 py-8 text-center text-gray-500">Loading...</td>
|
<td colspan="8" class="px-6 py-8 text-center text-gray-500">Loading...</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -143,10 +182,10 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Job Dashboard Modal -->
|
<!-- Job Dashboard Modal -->
|
||||||
<div id="jobModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto z-50">
|
<div id="jobModal" class="hidden fixed inset-0 backdrop-blur-sm bg-black/20 overflow-y-auto z-50">
|
||||||
<div class="relative top-0 sm:top-10 mx-auto p-4 sm:p-6 border-0 sm:border w-full sm:max-w-7xl shadow-lg rounded-none sm:rounded-md bg-gray-50 mb-0 sm:mb-10 min-h-screen sm:min-h-[600px]">
|
<div class="relative top-0 sm:top-10 mx-auto p-4 sm:p-6 w-full sm:max-w-7xl shadow-2xl rounded-none sm:rounded-3xl backdrop-blur-2xl bg-white/80 border border-white/30 mb-0 sm:mb-10 min-h-screen sm:min-h-[600px]">
|
||||||
<!-- Dashboard Header -->
|
<!-- Dashboard Header -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 mb-6">
|
||||||
<div class="flex flex-col lg:flex-row justify-between items-start gap-4 mb-4">
|
<div class="flex flex-col lg:flex-row justify-between items-start gap-4 mb-4">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<h2 class="text-2xl font-bold text-gray-900 mb-2" id="dashboardJobName">Job Dashboard</h2>
|
<h2 class="text-2xl font-bold text-gray-900 mb-2" id="dashboardJobName">Job Dashboard</h2>
|
||||||
@@ -163,7 +202,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Quick Info Card -->
|
<!-- Quick Info Card -->
|
||||||
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200 min-w-[250px]">
|
<div class="backdrop-blur-xl bg-white/40 rounded-2xl p-4 border border-white/30 min-w-[250px] shadow-lg">
|
||||||
<h3 class="text-sm font-semibold text-gray-900 mb-3">Quick Info</h3>
|
<h3 class="text-sm font-semibold text-gray-900 mb-3">Quick Info</h3>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
@@ -215,28 +254,28 @@
|
|||||||
<!-- Overview Tab Content -->
|
<!-- Overview Tab Content -->
|
||||||
<div id="dashboard-tab-overview" class="dashboard-tab-content space-y-6 min-h-[500px]">
|
<div id="dashboard-tab-overview" class="dashboard-tab-content space-y-6 min-h-[500px]">
|
||||||
<!-- Basic Information Card -->
|
<!-- Basic Information Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Basic Information</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Basic Information</h3>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Number" class="block text-sm font-medium text-gray-700 mb-1">Job Number</label>
|
<label for="Job_Number" class="block text-sm font-medium text-gray-700 mb-1">Job Number</label>
|
||||||
<input type="text" id="Job_Number" name="Job_Number" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Job_Number" name="Job_Number" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Full_Name" class="block text-sm font-medium text-gray-700 mb-1">Job Full Name</label>
|
<label for="Job_Full_Name" class="block text-sm font-medium text-gray-700 mb-1">Job Full Name</label>
|
||||||
<input type="text" id="Job_Full_Name" name="Job_Full_Name" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Job_Full_Name" name="Job_Full_Name" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Name" class="block text-sm font-medium text-gray-700 mb-1">Job Name</label>
|
<label for="Job_Name" class="block text-sm font-medium text-gray-700 mb-1">Job Name</label>
|
||||||
<input type="text" id="Job_Name" name="Job_Name" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Job_Name" name="Job_Name" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Address" class="block text-sm font-medium text-gray-700 mb-1">Job Address</label>
|
<label for="Job_Address" class="block text-sm font-medium text-gray-700 mb-1">Job Address</label>
|
||||||
<input type="text" id="Job_Address" name="Job_Address" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Job_Address" name="Job_Address" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Type" class="block text-sm font-medium text-gray-700 mb-1">Job Type</label>
|
<label for="Job_Type" class="block text-sm font-medium text-gray-700 mb-1">Job Type</label>
|
||||||
<select id="Job_Type" name="Job_Type" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="Job_Type" name="Job_Type" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select type...</option>
|
<option value="">Select type...</option>
|
||||||
<option value="FX">FX</option>
|
<option value="FX">FX</option>
|
||||||
<option value="TM">TM</option>
|
<option value="TM">TM</option>
|
||||||
@@ -248,7 +287,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Status" class="block text-sm font-medium text-gray-700 mb-1">Job Status</label>
|
<label for="Job_Status" class="block text-sm font-medium text-gray-700 mb-1">Job Status</label>
|
||||||
<select id="Job_Status" name="Job_Status" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="Job_Status" name="Job_Status" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select status...</option>
|
<option value="">Select status...</option>
|
||||||
<option value="Estimating">Estimating</option>
|
<option value="Estimating">Estimating</option>
|
||||||
<option value="Est Sent">Est Sent</option>
|
<option value="Est Sent">Est Sent</option>
|
||||||
@@ -266,7 +305,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Division" class="block text-sm font-medium text-gray-700 mb-1">Job Division</label>
|
<label for="Job_Division" class="block text-sm font-medium text-gray-700 mb-1">Job Division</label>
|
||||||
<select id="Job_Division" name="Job_Division" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="Job_Division" name="Job_Division" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select division...</option>
|
<option value="">Select division...</option>
|
||||||
<option value="C#">C#</option>
|
<option value="C#">C#</option>
|
||||||
<option value="R#">R#</option>
|
<option value="R#">R#</option>
|
||||||
@@ -280,12 +319,12 @@
|
|||||||
<!-- Team & Client Tab Content -->
|
<!-- Team & Client Tab Content -->
|
||||||
<div id="dashboard-tab-team" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
<div id="dashboard-tab-team" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
||||||
<!-- Team Assignment Card -->
|
<!-- Team Assignment Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Team Assignment</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Team Assignment</h3>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="Estimator" class="block text-sm font-medium text-gray-700 mb-1">Estimator</label>
|
<label for="Estimator" class="block text-sm font-medium text-gray-700 mb-1">Estimator</label>
|
||||||
<select id="Estimator" name="Estimator" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="Estimator" name="Estimator" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select estimator...</option>
|
<option value="">Select estimator...</option>
|
||||||
<option value="Beth Cardoza">Beth Cardoza</option>
|
<option value="Beth Cardoza">Beth Cardoza</option>
|
||||||
<option value="Steve Brewer">Steve Brewer</option>
|
<option value="Steve Brewer">Steve Brewer</option>
|
||||||
@@ -294,7 +333,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Office_Rep" class="block text-sm font-medium text-gray-700 mb-1">Office Rep</label>
|
<label for="Office_Rep" class="block text-sm font-medium text-gray-700 mb-1">Office Rep</label>
|
||||||
<select id="Office_Rep" name="Office_Rep" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="Office_Rep" name="Office_Rep" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select office rep...</option>
|
<option value="">Select office rep...</option>
|
||||||
<option value="Regina McClain">Regina McClain</option>
|
<option value="Regina McClain">Regina McClain</option>
|
||||||
<option value="Beth Cardoza">Beth Cardoza</option>
|
<option value="Beth Cardoza">Beth Cardoza</option>
|
||||||
@@ -304,30 +343,30 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Project_Manager" class="block text-sm font-medium text-gray-700 mb-1">Project Manager</label>
|
<label for="Project_Manager" class="block text-sm font-medium text-gray-700 mb-1">Project Manager</label>
|
||||||
<input type="text" id="Project_Manager" name="Project_Manager" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Project_Manager" name="Project_Manager" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Client Information Card -->
|
<!-- Client Information Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Client Information</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Client Information</h3>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="Company_Client" class="block text-sm font-medium text-gray-700 mb-1">Company Client</label>
|
<label for="Company_Client" class="block text-sm font-medium text-gray-700 mb-1">Company Client</label>
|
||||||
<input type="text" id="Company_Client" name="Company_Client" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Company_Client" name="Company_Client" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Contact_Person" class="block text-sm font-medium text-gray-700 mb-1">Contact Person</label>
|
<label for="Contact_Person" class="block text-sm font-medium text-gray-700 mb-1">Contact Person</label>
|
||||||
<input type="text" id="Contact_Person" name="Contact_Person" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Contact_Person" name="Contact_Person" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Phone_Number" class="block text-sm font-medium text-gray-700 mb-1">Phone Number</label>
|
<label for="Phone_Number" class="block text-sm font-medium text-gray-700 mb-1">Phone Number</label>
|
||||||
<input type="tel" id="Phone_Number" name="Phone_Number" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="tel" id="Phone_Number" name="Phone_Number" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
<label for="Email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||||
<input type="email" id="Email" name="Email" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="email" id="Email" name="Email" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -336,63 +375,63 @@
|
|||||||
<!-- Dates & Links Tab Content -->
|
<!-- Dates & Links Tab Content -->
|
||||||
<div id="dashboard-tab-dates" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
<div id="dashboard-tab-dates" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
||||||
<!-- Dates & Scheduling Card -->
|
<!-- Dates & Scheduling Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Dates & Scheduling</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Dates & Scheduling</h3>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="Start_Date" class="block text-sm font-medium text-gray-700 mb-1">Start Date</label>
|
<label for="Start_Date" class="block text-sm font-medium text-gray-700 mb-1">Start Date</label>
|
||||||
<input type="date" id="Start_Date" name="Start_Date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="date" id="Start_Date" name="Start_Date" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Due_Date" class="block text-sm font-medium text-gray-700 mb-1">Due Date</label>
|
<label for="Due_Date" class="block text-sm font-medium text-gray-700 mb-1">Due Date</label>
|
||||||
<input type="date" id="Due_Date" name="Due_Date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="date" id="Due_Date" name="Due_Date" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Submission_Date" class="block text-sm font-medium text-gray-700 mb-1">Submission Date</label>
|
<label for="Submission_Date" class="block text-sm font-medium text-gray-700 mb-1">Submission Date</label>
|
||||||
<input type="date" id="Submission_Date" name="Submission_Date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="date" id="Submission_Date" name="Submission_Date" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Due_Date_Source" class="block text-sm font-medium text-gray-700 mb-1">Due Date Source</label>
|
<label for="Due_Date_Source" class="block text-sm font-medium text-gray-700 mb-1">Due Date Source</label>
|
||||||
<input type="text" id="Due_Date_Source" name="Due_Date_Source" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Due_Date_Source" name="Due_Date_Source" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Due_Date_Counter" class="block text-sm font-medium text-gray-700 mb-1">Due Date Counter</label>
|
<label for="Due_Date_Counter" class="block text-sm font-medium text-gray-700 mb-1">Due Date Counter</label>
|
||||||
<input type="text" id="Due_Date_Counter" name="Due_Date_Counter" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Due_Date_Counter" name="Due_Date_Counter" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Due_Time" class="block text-sm font-medium text-gray-700 mb-1">Due Time</label>
|
<label for="Due_Time" class="block text-sm font-medium text-gray-700 mb-1">Due Time</label>
|
||||||
<input type="time" id="Due_Time" name="Due_Time" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="time" id="Due_Time" name="Due_Time" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Schedule_Confidence" class="block text-sm font-medium text-gray-700 mb-1">Schedule Confidence</label>
|
<label for="Schedule_Confidence" class="block text-sm font-medium text-gray-700 mb-1">Schedule Confidence</label>
|
||||||
<input type="text" id="Schedule_Confidence" name="Schedule_Confidence" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Schedule_Confidence" name="Schedule_Confidence" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Links & References Card -->
|
<!-- Links & References Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Links & References</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Links & References</h3>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_QB_Link" class="block text-sm font-medium text-gray-700 mb-1">Job QB Link</label>
|
<label for="Job_QB_Link" class="block text-sm font-medium text-gray-700 mb-1">Job QB Link</label>
|
||||||
<input type="url" id="Job_QB_Link" name="Job_QB_Link" placeholder="https://..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="url" id="Job_QB_Link" name="Job_QB_Link" placeholder="https://..." class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Voxer_Link" class="block text-sm font-medium text-gray-700 mb-1">Voxer Link</label>
|
<label for="Voxer_Link" class="block text-sm font-medium text-gray-700 mb-1">Voxer Link</label>
|
||||||
<input type="url" id="Voxer_Link" name="Voxer_Link" placeholder="https://..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="url" id="Voxer_Link" name="Voxer_Link" placeholder="https://..." class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Folder_Link" class="block text-sm font-medium text-gray-700 mb-1">Job Folder Link</label>
|
<label for="Job_Folder_Link" class="block text-sm font-medium text-gray-700 mb-1">Job Folder Link</label>
|
||||||
<input type="url" id="Job_Folder_Link" name="Job_Folder_Link" placeholder="https://..." class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="url" id="Job_Folder_Link" name="Job_Folder_Link" placeholder="https://..." class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Codes" class="block text-sm font-medium text-gray-700 mb-1">Job Codes</label>
|
<label for="Job_Codes" class="block text-sm font-medium text-gray-700 mb-1">Job Codes</label>
|
||||||
<input type="text" id="Job_Codes" name="Job_Codes" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Job_Codes" name="Job_Codes" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="Job_Number_Parent" class="block text-sm font-medium text-gray-700 mb-1">Job Number Parent</label>
|
<label for="Job_Number_Parent" class="block text-sm font-medium text-gray-700 mb-1">Job Number Parent</label>
|
||||||
<input type="text" id="Job_Number_Parent" name="Job_Number_Parent" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="text" id="Job_Number_Parent" name="Job_Number_Parent" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -401,7 +440,7 @@
|
|||||||
<!-- Status Flags Tab Content -->
|
<!-- Status Flags Tab Content -->
|
||||||
<div id="dashboard-tab-flags" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
<div id="dashboard-tab-flags" class="dashboard-tab-content hidden space-y-6 min-h-[500px]">
|
||||||
<!-- Status Flags Card -->
|
<!-- Status Flags Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Status Flags</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Status Flags</h3>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -459,7 +498,7 @@
|
|||||||
<!-- Notes Tab Content -->
|
<!-- Notes Tab Content -->
|
||||||
<div id="dashboard-tab-notes" class="dashboard-tab-content hidden min-h-[500px]">
|
<div id="dashboard-tab-notes" class="dashboard-tab-content hidden min-h-[500px]">
|
||||||
<!-- Notes Card -->
|
<!-- Notes Card -->
|
||||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h3 class="text-lg font-semibold text-gray-900">Notes</h3>
|
<h3 class="text-lg font-semibold text-gray-900">Notes</h3>
|
||||||
<button
|
<button
|
||||||
@@ -483,7 +522,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="noteSentiment" class="block text-sm font-medium text-gray-700 mb-1">Sentiment</label>
|
<label for="noteSentiment" class="block text-sm font-medium text-gray-700 mb-1">Sentiment</label>
|
||||||
<select id="noteSentiment" name="noteSentiment" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm">
|
<select id="noteSentiment" name="noteSentiment" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-sm transition-all shadow-sm">
|
||||||
<option value="">Select sentiment...</option>
|
<option value="">Select sentiment...</option>
|
||||||
<option value="Positive">Positive</option>
|
<option value="Positive">Positive</option>
|
||||||
<option value="Negative">Negative</option>
|
<option value="Negative">Negative</option>
|
||||||
@@ -517,13 +556,13 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick="closeJobModal()"
|
onclick="closeJobModal()"
|
||||||
class="w-full sm:w-auto px-6 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-lg font-medium hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition shadow-sm"
|
class="w-full sm:w-auto px-6 py-2.5 backdrop-blur-sm bg-white/60 border-2 border-gray-300/60 text-gray-700 rounded-xl font-medium hover:bg-white/80 focus:outline-none focus:ring-2 focus:ring-gray-500/50 transition-all shadow-lg hover:shadow-xl"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="w-full sm:w-auto px-6 py-2.5 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition shadow-sm"
|
class="w-full sm:w-auto px-6 py-2.5 backdrop-blur-sm bg-indigo-600/90 text-white rounded-xl font-medium hover:bg-indigo-700/90 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition-all shadow-lg hover:shadow-xl"
|
||||||
>
|
>
|
||||||
Save Job
|
Save Job
|
||||||
</button>
|
</button>
|
||||||
@@ -536,6 +575,8 @@
|
|||||||
<script src="js/pocketbase.js"></script>
|
<script src="js/pocketbase.js"></script>
|
||||||
<script src="js/config.js"></script>
|
<script src="js/config.js"></script>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/preferences.js"></script>
|
||||||
|
<script src="js/custom-dropdown.js"></script>
|
||||||
<script src="js/jobs.js"></script>
|
<script src="js/jobs.js"></script>
|
||||||
<script src="js/notes.js"></script>
|
<script src="js/notes.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -544,12 +585,43 @@
|
|||||||
// Check authentication
|
// Check authentication
|
||||||
checkAuth();
|
checkAuth();
|
||||||
|
|
||||||
// Set user email
|
// Initialize dark mode
|
||||||
|
initializeDarkMode();
|
||||||
|
|
||||||
|
// Set user email and initials
|
||||||
const user = pb.authStore.model;
|
const user = pb.authStore.model;
|
||||||
if (user) {
|
if (user) {
|
||||||
document.getElementById('userEmail').textContent = user.email || '';
|
const email = user.email || '';
|
||||||
|
document.getElementById('userEmail').textContent = email;
|
||||||
|
|
||||||
|
// Set initials for avatar
|
||||||
|
const initials = email
|
||||||
|
.split('@')[0]
|
||||||
|
.split('.')
|
||||||
|
.map(part => part.charAt(0).toUpperCase())
|
||||||
|
.join('')
|
||||||
|
.substring(0, 2) || 'U';
|
||||||
|
document.getElementById('userInitials').textContent = initials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toggle user dropdown
|
||||||
|
function toggleUserDropdown() {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
dropdown.classList.toggle('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
const button = document.getElementById('userAvatarBtn');
|
||||||
|
if (dropdown && !dropdown.contains(e.target) && !button.contains(e.target)) {
|
||||||
|
dropdown.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make function globally accessible
|
||||||
|
window.toggleUserDropdown = toggleUserDropdown;
|
||||||
|
|
||||||
// Initialize Quill editor for notes
|
// Initialize Quill editor for notes
|
||||||
function initNoteEditor() {
|
function initNoteEditor() {
|
||||||
if (!noteEditor) {
|
if (!noteEditor) {
|
||||||
@@ -628,6 +700,18 @@
|
|||||||
// Show notes tab for existing jobs
|
// Show notes tab for existing jobs
|
||||||
document.getElementById('notesTabButton').classList.remove('hidden');
|
document.getElementById('notesTabButton').classList.remove('hidden');
|
||||||
initNoteEditor();
|
initNoteEditor();
|
||||||
|
// Initialize custom dropdowns after loading job data
|
||||||
|
setTimeout(() => {
|
||||||
|
if (typeof initCustomDropdowns === 'function') {
|
||||||
|
initCustomDropdowns();
|
||||||
|
}
|
||||||
|
// Update dropdown values
|
||||||
|
['Job_Type', 'Job_Status', 'Job_Division', 'Estimator', 'Office_Rep'].forEach(fieldId => {
|
||||||
|
if (typeof updateCustomDropdown === 'function') {
|
||||||
|
updateCustomDropdown(fieldId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('jobForm').reset();
|
document.getElementById('jobForm').reset();
|
||||||
@@ -637,6 +721,12 @@
|
|||||||
document.getElementById('dashboardJobStatus').textContent = '-';
|
document.getElementById('dashboardJobStatus').textContent = '-';
|
||||||
// Hide notes tab for new jobs
|
// Hide notes tab for new jobs
|
||||||
document.getElementById('notesTabButton').classList.add('hidden');
|
document.getElementById('notesTabButton').classList.add('hidden');
|
||||||
|
// Initialize custom dropdowns for new job
|
||||||
|
setTimeout(() => {
|
||||||
|
if (typeof initCustomDropdowns === 'function') {
|
||||||
|
initCustomDropdowns();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
// Start with overview tab
|
// Start with overview tab
|
||||||
switchDashboardTab('overview');
|
switchDashboardTab('overview');
|
||||||
@@ -895,8 +985,20 @@
|
|||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// Wait for config to load
|
// Wait for config to load
|
||||||
await initConfig();
|
await initConfig();
|
||||||
|
// Load default view from preferences
|
||||||
|
if (typeof getUserPreferences !== 'undefined') {
|
||||||
|
const prefs = await getUserPreferences();
|
||||||
|
if (prefs.defaultView) {
|
||||||
|
currentView = prefs.defaultView;
|
||||||
|
localStorage.setItem('jobsView', currentView);
|
||||||
|
}
|
||||||
|
}
|
||||||
await loadEstimatorFilters();
|
await loadEstimatorFilters();
|
||||||
await loadStatusFilters();
|
await loadStatusFilters();
|
||||||
|
// Initialize custom dropdowns
|
||||||
|
if (typeof initCustomDropdowns === 'function') {
|
||||||
|
initCustomDropdowns();
|
||||||
|
}
|
||||||
// Initialize view display
|
// Initialize view display
|
||||||
if (typeof updateViewDisplay === 'function') {
|
if (typeof updateViewDisplay === 'function') {
|
||||||
updateViewDisplay();
|
updateViewDisplay();
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,7 @@ async function login(email, password) {
|
|||||||
*/
|
*/
|
||||||
function logout() {
|
function logout() {
|
||||||
pb.authStore.clear();
|
pb.authStore.clear();
|
||||||
window.location.href = 'index.html';
|
window.location.href = 'index';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,7 +26,7 @@ function logout() {
|
|||||||
*/
|
*/
|
||||||
function checkAuth() {
|
function checkAuth() {
|
||||||
if (!pb.authStore.isValid) {
|
if (!pb.authStore.isValid) {
|
||||||
window.location.href = 'index.html';
|
window.location.href = 'index';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
// Custom dropdown component with liquid glass styling
|
||||||
|
// Replaces native select elements with styled custom dropdowns
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize custom dropdowns for all select elements
|
||||||
|
*/
|
||||||
|
function initCustomDropdowns() {
|
||||||
|
const selects = document.querySelectorAll('select');
|
||||||
|
selects.forEach(select => {
|
||||||
|
// Skip if already converted
|
||||||
|
if (select.dataset.customDropdown === 'true') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createCustomDropdown(select);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a custom dropdown for a select element
|
||||||
|
*/
|
||||||
|
function createCustomDropdown(select) {
|
||||||
|
// Mark as converted
|
||||||
|
select.dataset.customDropdown = 'true';
|
||||||
|
|
||||||
|
// Create dropdown container
|
||||||
|
const dropdownContainer = document.createElement('div');
|
||||||
|
dropdownContainer.className = 'relative';
|
||||||
|
|
||||||
|
// Create button
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = 'button';
|
||||||
|
button.className = 'w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm text-left flex items-center justify-between';
|
||||||
|
button.id = select.id + '_button';
|
||||||
|
|
||||||
|
// Get selected option text
|
||||||
|
const selectedOption = select.options[select.selectedIndex];
|
||||||
|
const buttonText = document.createElement('span');
|
||||||
|
buttonText.className = 'flex-1 text-gray-900';
|
||||||
|
buttonText.textContent = selectedOption ? selectedOption.text : 'Select...';
|
||||||
|
button.appendChild(buttonText);
|
||||||
|
|
||||||
|
// Add chevron icon
|
||||||
|
const chevron = document.createElement('svg');
|
||||||
|
chevron.className = 'w-5 h-5 text-gray-400 flex-shrink-0 ml-2 transition-transform';
|
||||||
|
chevron.id = select.id + '_chevron';
|
||||||
|
chevron.fill = 'none';
|
||||||
|
chevron.stroke = 'currentColor';
|
||||||
|
chevron.viewBox = '0 0 24 24';
|
||||||
|
chevron.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>';
|
||||||
|
button.appendChild(chevron);
|
||||||
|
|
||||||
|
// Create dropdown menu
|
||||||
|
const menu = document.createElement('div');
|
||||||
|
menu.className = 'hidden absolute z-50 w-full mt-1 backdrop-blur-xl bg-white/80 rounded-2xl shadow-2xl border border-white/30 overflow-hidden max-h-60 overflow-y-auto';
|
||||||
|
menu.id = select.id + '_menu';
|
||||||
|
|
||||||
|
// Create options
|
||||||
|
Array.from(select.options).forEach((option, index) => {
|
||||||
|
const menuItem = document.createElement('button');
|
||||||
|
menuItem.type = 'button';
|
||||||
|
menuItem.className = 'w-full text-left px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||||
|
if (option.value === select.value) {
|
||||||
|
menuItem.className += ' bg-indigo-50/50 text-indigo-600';
|
||||||
|
}
|
||||||
|
menuItem.textContent = option.text;
|
||||||
|
menuItem.dataset.value = option.value;
|
||||||
|
|
||||||
|
menuItem.addEventListener('click', () => {
|
||||||
|
select.value = option.value;
|
||||||
|
buttonText.textContent = option.text;
|
||||||
|
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
closeDropdown(select.id);
|
||||||
|
|
||||||
|
// Update selected state
|
||||||
|
menu.querySelectorAll('button').forEach(item => {
|
||||||
|
if (item.dataset.value === option.value) {
|
||||||
|
item.className = 'w-full text-left px-4 py-2.5 text-sm text-indigo-600 bg-indigo-50/50 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||||
|
} else {
|
||||||
|
item.className = 'w-full text-left px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.appendChild(menuItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle dropdown
|
||||||
|
button.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const isOpen = !menu.classList.contains('hidden');
|
||||||
|
|
||||||
|
// Close all other dropdowns
|
||||||
|
document.querySelectorAll('[id$="_menu"]').forEach(m => {
|
||||||
|
if (m.id !== menu.id) {
|
||||||
|
m.classList.add('hidden');
|
||||||
|
const otherChevron = document.getElementById(m.id.replace('_menu', '_chevron'));
|
||||||
|
if (otherChevron) {
|
||||||
|
otherChevron.classList.remove('rotate-180');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
closeDropdown(select.id);
|
||||||
|
} else {
|
||||||
|
openDropdown(select.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on outside click
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!dropdownContainer.contains(e.target)) {
|
||||||
|
closeDropdown(select.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wrap select and add custom elements
|
||||||
|
select.style.display = 'none';
|
||||||
|
select.style.position = 'absolute';
|
||||||
|
select.style.opacity = '0';
|
||||||
|
select.style.pointerEvents = 'none';
|
||||||
|
dropdownContainer.appendChild(button);
|
||||||
|
dropdownContainer.appendChild(menu);
|
||||||
|
select.parentNode.insertBefore(dropdownContainer, select);
|
||||||
|
dropdownContainer.appendChild(select);
|
||||||
|
|
||||||
|
// Listen for programmatic changes to select
|
||||||
|
select.addEventListener('change', () => {
|
||||||
|
updateCustomDropdown(select.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open dropdown
|
||||||
|
*/
|
||||||
|
function openDropdown(selectId) {
|
||||||
|
const menu = document.getElementById(selectId + '_menu');
|
||||||
|
const chevron = document.getElementById(selectId + '_chevron');
|
||||||
|
if (menu) {
|
||||||
|
menu.classList.remove('hidden');
|
||||||
|
if (chevron) {
|
||||||
|
chevron.classList.add('rotate-180');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close dropdown
|
||||||
|
*/
|
||||||
|
function closeDropdown(selectId) {
|
||||||
|
const menu = document.getElementById(selectId + '_menu');
|
||||||
|
const chevron = document.getElementById(selectId + '_chevron');
|
||||||
|
if (menu) {
|
||||||
|
menu.classList.add('hidden');
|
||||||
|
if (chevron) {
|
||||||
|
chevron.classList.remove('rotate-180');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update custom dropdown when select value changes programmatically
|
||||||
|
*/
|
||||||
|
function updateCustomDropdown(selectId) {
|
||||||
|
const select = document.getElementById(selectId);
|
||||||
|
if (!select || select.dataset.customDropdown !== 'true') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const button = document.getElementById(selectId + '_button');
|
||||||
|
const buttonText = button?.querySelector('span');
|
||||||
|
const selectedOption = select.options[select.selectedIndex];
|
||||||
|
|
||||||
|
if (buttonText && selectedOption) {
|
||||||
|
buttonText.textContent = selectedOption.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update selected state in menu
|
||||||
|
const menu = document.getElementById(selectId + '_menu');
|
||||||
|
if (menu) {
|
||||||
|
menu.querySelectorAll('button').forEach(item => {
|
||||||
|
if (item.dataset.value === select.value) {
|
||||||
|
item.className = 'w-full text-left px-4 py-2.5 text-sm text-indigo-600 bg-indigo-50/50 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||||
|
} else {
|
||||||
|
item.className = 'w-full text-left px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make functions globally accessible
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.initCustomDropdowns = initCustomDropdowns;
|
||||||
|
window.createCustomDropdown = createCustomDropdown;
|
||||||
|
window.updateCustomDropdown = updateCustomDropdown;
|
||||||
|
}
|
||||||
+43
-9
@@ -208,7 +208,7 @@ function displayJobsCards(jobs) {
|
|||||||
return `
|
return `
|
||||||
<div
|
<div
|
||||||
onclick="openJobModal('${job.id}')"
|
onclick="openJobModal('${job.id}')"
|
||||||
class="bg-white rounded-lg shadow border border-gray-200 p-6 hover:shadow-lg hover:border-indigo-300 cursor-pointer transition-all duration-200 hover:bg-indigo-50/30"
|
class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6 hover:shadow-2xl hover:border-white/50 hover:bg-white/70 cursor-pointer transition-all duration-300 hover:scale-[1.02]"
|
||||||
>
|
>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
@@ -226,6 +226,7 @@ function displayJobsCards(jobs) {
|
|||||||
<div>
|
<div>
|
||||||
<span class="text-xs text-gray-500 uppercase tracking-wide">Client</span>
|
<span class="text-xs text-gray-500 uppercase tracking-wide">Client</span>
|
||||||
<p class="text-sm font-medium text-gray-900 mt-1">${job.Company_Client || '-'}</p>
|
<p class="text-sm font-medium text-gray-900 mt-1">${job.Company_Client || '-'}</p>
|
||||||
|
${job.Phone_Number ? `<a href="tel:${job.Phone_Number.replace(/[^\d+]/g, '')}" onclick="event.stopPropagation()" class="text-xs text-indigo-600 hover:text-indigo-800 mt-1 block hover:underline">${job.Phone_Number}</a>` : ''}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -242,6 +243,31 @@ function displayJobsCards(jobs) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${job.Job_Address ? `
|
||||||
|
<div>
|
||||||
|
<span class="text-xs text-gray-500 uppercase tracking-wide">Address</span>
|
||||||
|
<p class="text-sm font-medium text-gray-900 mt-1">${job.Job_Address}</p>
|
||||||
|
<div class="mt-2 flex flex-col gap-2">
|
||||||
|
<a
|
||||||
|
href="https://maps.apple.com/?q=${encodeURIComponent(job.Job_Address)}"
|
||||||
|
target="_blank"
|
||||||
|
onclick="event.stopPropagation()"
|
||||||
|
class="w-full px-3 py-1.5 backdrop-blur-sm bg-gray-900/90 text-white text-xs font-medium rounded-xl hover:bg-gray-800/90 transition-all shadow-lg hover:shadow-xl text-center"
|
||||||
|
>
|
||||||
|
Apple Maps
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(job.Job_Address)}"
|
||||||
|
target="_blank"
|
||||||
|
onclick="event.stopPropagation()"
|
||||||
|
class="w-full px-3 py-1.5 backdrop-blur-sm bg-blue-600/90 text-white text-xs font-medium rounded-xl hover:bg-blue-700/90 transition-all shadow-lg hover:shadow-xl text-center"
|
||||||
|
>
|
||||||
|
Google Maps
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4 sm:col-span-2 lg:col-span-1">
|
<div class="grid grid-cols-2 gap-4 sm:col-span-2 lg:col-span-1">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-xs text-gray-500 uppercase tracking-wide">Type</span>
|
<span class="text-xs text-gray-500 uppercase tracking-wide">Type</span>
|
||||||
@@ -278,26 +304,34 @@ function updateViewDisplay() {
|
|||||||
const cardViewBtn = document.getElementById('cardViewBtn');
|
const cardViewBtn = document.getElementById('cardViewBtn');
|
||||||
|
|
||||||
if (currentView === 'table') {
|
if (currentView === 'table') {
|
||||||
|
// Show table, hide card
|
||||||
if (tableView) tableView.classList.remove('hidden');
|
if (tableView) tableView.classList.remove('hidden');
|
||||||
if (cardView) cardView.classList.add('hidden');
|
if (cardView) cardView.classList.add('hidden');
|
||||||
|
|
||||||
|
// Table button: active (indigo with shadow)
|
||||||
if (tableViewBtn) {
|
if (tableViewBtn) {
|
||||||
tableViewBtn.classList.add('bg-indigo-600', 'text-white');
|
tableViewBtn.classList.remove('text-gray-700', 'hover:bg-white/40');
|
||||||
tableViewBtn.classList.remove('text-gray-700', 'hover:bg-gray-50');
|
tableViewBtn.classList.add('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90');
|
||||||
}
|
}
|
||||||
|
// Card button: inactive (transparent)
|
||||||
if (cardViewBtn) {
|
if (cardViewBtn) {
|
||||||
cardViewBtn.classList.remove('bg-indigo-600', 'text-white');
|
cardViewBtn.classList.remove('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90');
|
||||||
cardViewBtn.classList.add('text-gray-700', 'hover:bg-gray-50');
|
cardViewBtn.classList.add('text-gray-700', 'hover:bg-white/40');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Show card, hide table
|
||||||
if (tableView) tableView.classList.add('hidden');
|
if (tableView) tableView.classList.add('hidden');
|
||||||
if (cardView) cardView.classList.remove('hidden');
|
if (cardView) cardView.classList.remove('hidden');
|
||||||
|
|
||||||
|
// Table button: inactive (transparent)
|
||||||
if (tableViewBtn) {
|
if (tableViewBtn) {
|
||||||
tableViewBtn.classList.remove('bg-indigo-600', 'text-white');
|
tableViewBtn.classList.remove('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90');
|
||||||
tableViewBtn.classList.add('text-gray-700', 'hover:bg-gray-50');
|
tableViewBtn.classList.add('text-gray-700', 'hover:bg-white/40');
|
||||||
}
|
}
|
||||||
|
// Card button: active (indigo with shadow)
|
||||||
if (cardViewBtn) {
|
if (cardViewBtn) {
|
||||||
cardViewBtn.classList.add('bg-indigo-600', 'text-white');
|
cardViewBtn.classList.remove('text-gray-700', 'hover:bg-white/40');
|
||||||
cardViewBtn.classList.remove('text-gray-700', 'hover:bg-gray-50');
|
cardViewBtn.classList.add('bg-indigo-600/90', 'text-white', 'shadow-lg', 'hover:bg-indigo-700/90');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,267 @@
|
|||||||
|
// User preferences management
|
||||||
|
// Stores preferences in the users collection PRISM column as JSON
|
||||||
|
// Uses "Job-Pages" key to avoid conflicts with other applications
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user preferences from PRISM column
|
||||||
|
*/
|
||||||
|
async function getUserPreferences() {
|
||||||
|
try {
|
||||||
|
const user = pb.authStore.model;
|
||||||
|
if (!user) {
|
||||||
|
return getDefaultPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current user record
|
||||||
|
const userRecord = await pb.collection('users').getOne(user.id);
|
||||||
|
|
||||||
|
// Parse PRISM JSON if it exists
|
||||||
|
let prism = {};
|
||||||
|
if (userRecord.PRISM) {
|
||||||
|
try {
|
||||||
|
prism = typeof userRecord.PRISM === 'string'
|
||||||
|
? JSON.parse(userRecord.PRISM)
|
||||||
|
: userRecord.PRISM;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error parsing PRISM data:', e);
|
||||||
|
prism = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Job-Pages preferences (isolated from other apps)
|
||||||
|
const jobPagesPrefs = prism['Job-Pages'] || {};
|
||||||
|
|
||||||
|
// Merge with defaults
|
||||||
|
return {
|
||||||
|
darkMode: jobPagesPrefs.darkMode ?? false,
|
||||||
|
defaultView: jobPagesPrefs.defaultView ?? 'table'
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading preferences:', error);
|
||||||
|
return getDefaultPreferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get default preferences
|
||||||
|
*/
|
||||||
|
function getDefaultPreferences() {
|
||||||
|
return {
|
||||||
|
darkMode: false,
|
||||||
|
defaultView: 'table'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save user preferences to PRISM column
|
||||||
|
*/
|
||||||
|
async function saveUserPreferences(preferences) {
|
||||||
|
try {
|
||||||
|
const user = pb.authStore.model;
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current user record
|
||||||
|
const userRecord = await pb.collection('users').getOne(user.id);
|
||||||
|
|
||||||
|
// Parse existing PRISM JSON
|
||||||
|
let prism = {};
|
||||||
|
if (userRecord.PRISM) {
|
||||||
|
try {
|
||||||
|
prism = typeof userRecord.PRISM === 'string'
|
||||||
|
? JSON.parse(userRecord.PRISM)
|
||||||
|
: userRecord.PRISM;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error parsing PRISM data:', e);
|
||||||
|
prism = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update only Job-Pages preferences (preserve other app data)
|
||||||
|
prism['Job-Pages'] = {
|
||||||
|
darkMode: preferences.darkMode,
|
||||||
|
defaultView: preferences.defaultView
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save back to PRISM column
|
||||||
|
await pb.collection('users').update(user.id, {
|
||||||
|
PRISM: JSON.stringify(prism)
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving preferences:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply dark mode to the page
|
||||||
|
*/
|
||||||
|
function applyDarkMode(enabled) {
|
||||||
|
const html = document.documentElement;
|
||||||
|
if (enabled) {
|
||||||
|
html.classList.add('dark');
|
||||||
|
localStorage.setItem('darkMode', 'true');
|
||||||
|
} else {
|
||||||
|
html.classList.remove('dark');
|
||||||
|
localStorage.setItem('darkMode', 'false');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize dark mode from preferences
|
||||||
|
*/
|
||||||
|
async function initializeDarkMode() {
|
||||||
|
try {
|
||||||
|
const preferences = await getUserPreferences();
|
||||||
|
applyDarkMode(preferences.darkMode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing dark mode:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update view option visual state
|
||||||
|
*/
|
||||||
|
function updateViewOptionVisuals() {
|
||||||
|
const selectedView = document.querySelector('input[name="defaultView"]:checked')?.value || 'table';
|
||||||
|
const options = document.querySelectorAll('.view-option');
|
||||||
|
|
||||||
|
options.forEach(option => {
|
||||||
|
const input = option.querySelector('input[type="radio"]');
|
||||||
|
const icon = option.querySelector('.view-icon');
|
||||||
|
const title = option.querySelector('.view-title');
|
||||||
|
const isSelected = input.value === selectedView;
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
option.classList.add('border-indigo-500', 'bg-indigo-50/50', 'shadow-md');
|
||||||
|
option.classList.remove('border-white/30', 'bg-white/40');
|
||||||
|
if (icon) icon.classList.add('text-indigo-600');
|
||||||
|
if (icon) icon.classList.remove('text-gray-400');
|
||||||
|
if (title) title.classList.add('text-indigo-600');
|
||||||
|
if (title) title.classList.remove('text-gray-900');
|
||||||
|
} else {
|
||||||
|
option.classList.remove('border-indigo-500', 'bg-indigo-50/50', 'shadow-md');
|
||||||
|
option.classList.add('border-white/30', 'bg-white/40');
|
||||||
|
if (icon) icon.classList.remove('text-indigo-600');
|
||||||
|
if (icon) icon.classList.add('text-gray-400');
|
||||||
|
if (title) title.classList.remove('text-indigo-600');
|
||||||
|
if (title) title.classList.add('text-gray-900');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load preferences and populate form
|
||||||
|
*/
|
||||||
|
async function loadPreferences() {
|
||||||
|
try {
|
||||||
|
const preferences = await getUserPreferences();
|
||||||
|
|
||||||
|
// Set dark mode toggle
|
||||||
|
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||||
|
if (darkModeToggle) {
|
||||||
|
darkModeToggle.checked = preferences.darkMode;
|
||||||
|
applyDarkMode(preferences.darkMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default view radio buttons
|
||||||
|
const viewTable = document.getElementById('viewTable');
|
||||||
|
const viewCard = document.getElementById('viewCard');
|
||||||
|
if (viewTable && viewCard) {
|
||||||
|
if (preferences.defaultView === 'table') {
|
||||||
|
viewTable.checked = true;
|
||||||
|
} else {
|
||||||
|
viewCard.checked = true;
|
||||||
|
}
|
||||||
|
// Update visual state
|
||||||
|
updateViewOptionVisuals();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add change listeners to radio buttons
|
||||||
|
const radioButtons = document.querySelectorAll('input[name="defaultView"]');
|
||||||
|
radioButtons.forEach(radio => {
|
||||||
|
radio.addEventListener('change', updateViewOptionVisuals);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading preferences:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save preferences from form
|
||||||
|
*/
|
||||||
|
async function savePreferences() {
|
||||||
|
try {
|
||||||
|
const darkMode = document.getElementById('darkModeToggle').checked;
|
||||||
|
const defaultView = document.querySelector('input[name="defaultView"]:checked')?.value || 'table';
|
||||||
|
|
||||||
|
const preferences = {
|
||||||
|
darkMode: darkMode,
|
||||||
|
defaultView: defaultView
|
||||||
|
};
|
||||||
|
|
||||||
|
await saveUserPreferences(preferences);
|
||||||
|
applyDarkMode(darkMode);
|
||||||
|
|
||||||
|
showMessage('Preferences saved successfully', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving preferences:', error);
|
||||||
|
showMessage('Error saving preferences: ' + (error.message || 'Unknown error'), 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show message to user
|
||||||
|
*/
|
||||||
|
function showMessage(message, type = 'success') {
|
||||||
|
const container = document.getElementById('messageContainer');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const bgColor = type === 'success'
|
||||||
|
? 'bg-green-50 border-green-200 text-green-700'
|
||||||
|
: 'bg-red-50 border-red-200 text-red-700';
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="${bgColor} border px-4 py-3 rounded-xl text-sm backdrop-blur-sm">
|
||||||
|
${message}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Auto-hide after 5 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
container.innerHTML = '';
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const form = document.getElementById('preferencesForm');
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await savePreferences();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode toggle change handler
|
||||||
|
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||||
|
if (darkModeToggle) {
|
||||||
|
darkModeToggle.addEventListener('change', (e) => {
|
||||||
|
applyDarkMode(e.target.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make functions globally accessible
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.getUserPreferences = getUserPreferences;
|
||||||
|
window.saveUserPreferences = saveUserPreferences;
|
||||||
|
window.initializeDarkMode = initializeDarkMode;
|
||||||
|
window.loadPreferences = loadPreferences;
|
||||||
|
window.savePreferences = savePreferences;
|
||||||
|
}
|
||||||
+69
-15
@@ -9,26 +9,48 @@
|
|||||||
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||||
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-gray-50">
|
<body class="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50/30 to-indigo-50/40">
|
||||||
<!-- Navigation Header -->
|
<!-- Navigation Header -->
|
||||||
<nav class="bg-white shadow-sm border-b border-gray-200">
|
<nav class="backdrop-blur-xl bg-white/70 border-b border-white/20 shadow-sm sticky top-0 z-40">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex flex-col sm:flex-row justify-between items-center h-16 sm:h-auto py-4 sm:py-0 gap-4 sm:gap-0">
|
<div class="flex flex-row justify-between items-center py-4 px-2">
|
||||||
<div class="flex items-center gap-4 w-full sm:w-auto">
|
<div class="flex items-center gap-4">
|
||||||
<a href="dashboard.html" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base">← Dashboard</a>
|
<a href="dashboard" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base transition-colors">← Dashboard</a>
|
||||||
<div class="text-lg sm:text-xl font-bold text-gray-800">Employee Reports</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row items-center gap-3 w-full sm:w-auto">
|
<div class="flex items-center gap-3 relative">
|
||||||
<div class="text-xs sm:text-sm text-gray-600" id="userEmail"></div>
|
<div class="relative">
|
||||||
|
<button
|
||||||
|
onclick="toggleUserDropdown()"
|
||||||
|
class="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex items-center justify-center text-white font-medium text-sm hover:scale-105 focus:outline-none transition-all duration-300 shadow-lg shadow-indigo-500/30 hover:shadow-indigo-500/50"
|
||||||
|
id="userAvatarBtn"
|
||||||
|
>
|
||||||
|
<span id="userInitials" class="text-xs font-semibold"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="userDropdown"
|
||||||
|
class="hidden absolute right-0 mt-2 w-48 backdrop-blur-xl bg-white/80 rounded-2xl shadow-2xl border border-white/30 py-2 z-50 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="px-4 py-3 border-b border-white/20">
|
||||||
|
<p class="text-xs text-gray-500 font-medium">Signed in as</p>
|
||||||
|
<p class="text-sm font-semibold text-gray-900 truncate mt-0.5" id="userEmail"></p>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="preferences"
|
||||||
|
class="block px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Preferences
|
||||||
|
</a>
|
||||||
<button
|
<button
|
||||||
onclick="logout()"
|
onclick="logout()"
|
||||||
class="w-full sm:w-auto bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition"
|
class="w-full text-left px-4 py-2.5 text-sm text-red-600 hover:bg-red-50/50 focus:outline-none transition-colors font-medium"
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@@ -57,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Reports Table Container -->
|
<!-- Reports Table Container -->
|
||||||
<div id="reportsTableContainer" class="hidden bg-white rounded-lg shadow border border-gray-200 overflow-hidden">
|
<div id="reportsTableContainer" class="hidden backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 overflow-hidden">
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="min-w-full divide-y divide-gray-200">
|
<table class="min-w-full divide-y divide-gray-200">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
@@ -115,7 +137,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="noteJob" class="block text-sm font-medium text-gray-700 mb-1">Job *</label>
|
<label for="noteJob" class="block text-sm font-medium text-gray-700 mb-1">Job *</label>
|
||||||
<select id="noteJob" name="job" required class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="noteJob" name="job" required class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select a job...</option>
|
<option value="">Select a job...</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,7 +150,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="noteSentiment" class="block text-sm font-medium text-gray-700 mb-1">Sentiment</label>
|
<label for="noteSentiment" class="block text-sm font-medium text-gray-700 mb-1">Sentiment</label>
|
||||||
<select id="noteSentiment" name="sentiment" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<select id="noteSentiment" name="sentiment" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
<option value="">Select sentiment...</option>
|
<option value="">Select sentiment...</option>
|
||||||
<option value="Positive">Positive</option>
|
<option value="Positive">Positive</option>
|
||||||
<option value="Negative">Negative</option>
|
<option value="Negative">Negative</option>
|
||||||
@@ -138,7 +160,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="noteDate" class="block text-sm font-medium text-gray-700 mb-1">Date & Time</label>
|
<label for="noteDate" class="block text-sm font-medium text-gray-700 mb-1">Date & Time</label>
|
||||||
<input type="datetime-local" id="noteDate" name="date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none text-base">
|
<input type="datetime-local" id="noteDate" name="date" class="w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col sm:flex-row justify-end space-y-2 sm:space-y-0 sm:space-x-3 pt-4 border-t">
|
<div class="flex flex-col sm:flex-row justify-end space-y-2 sm:space-y-0 sm:space-x-3 pt-4 border-t">
|
||||||
@@ -163,6 +185,7 @@
|
|||||||
<script src="js/pocketbase.js"></script>
|
<script src="js/pocketbase.js"></script>
|
||||||
<script src="js/config.js"></script>
|
<script src="js/config.js"></script>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/preferences.js"></script>
|
||||||
<script src="js/jobs.js"></script>
|
<script src="js/jobs.js"></script>
|
||||||
<script src="js/notes.js"></script>
|
<script src="js/notes.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -171,12 +194,43 @@
|
|||||||
// Check authentication
|
// Check authentication
|
||||||
checkAuth();
|
checkAuth();
|
||||||
|
|
||||||
// Set user email
|
// Initialize dark mode
|
||||||
|
initializeDarkMode();
|
||||||
|
|
||||||
|
// Set user email and initials
|
||||||
const user = pb.authStore.model;
|
const user = pb.authStore.model;
|
||||||
if (user) {
|
if (user) {
|
||||||
document.getElementById('userEmail').textContent = user.email || '';
|
const email = user.email || '';
|
||||||
|
document.getElementById('userEmail').textContent = email;
|
||||||
|
|
||||||
|
// Set initials for avatar
|
||||||
|
const initials = email
|
||||||
|
.split('@')[0]
|
||||||
|
.split('.')
|
||||||
|
.map(part => part.charAt(0).toUpperCase())
|
||||||
|
.join('')
|
||||||
|
.substring(0, 2) || 'U';
|
||||||
|
document.getElementById('userInitials').textContent = initials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toggle user dropdown
|
||||||
|
function toggleUserDropdown() {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
dropdown.classList.toggle('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
const button = document.getElementById('userAvatarBtn');
|
||||||
|
if (dropdown && !dropdown.contains(e.target) && !button.contains(e.target)) {
|
||||||
|
dropdown.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make function globally accessible
|
||||||
|
window.toggleUserDropdown = toggleUserDropdown;
|
||||||
|
|
||||||
// Initialize Quill editor
|
// Initialize Quill editor
|
||||||
noteEditor = new Quill('#noteEditor', {
|
noteEditor = new Quill('#noteEditor', {
|
||||||
theme: 'snow',
|
theme: 'snow',
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Job Tracking - Preferences</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/pocketbase@latest/dist/pocketbase.umd.js"></script>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50/30 to-indigo-50/40">
|
||||||
|
<!-- Navigation Header -->
|
||||||
|
<nav class="backdrop-blur-xl bg-white/70 border-b border-white/20 shadow-sm sticky top-0 z-40">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="flex flex-row justify-between items-center py-4 px-2">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<a href="dashboard" class="text-indigo-600 hover:text-indigo-700 font-medium text-sm sm:text-base transition-colors">← Dashboard</a>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3 relative">
|
||||||
|
<div class="relative">
|
||||||
|
<button
|
||||||
|
onclick="toggleUserDropdown()"
|
||||||
|
class="w-10 h-10 rounded-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex items-center justify-center text-white font-medium text-sm hover:scale-105 focus:outline-none transition-all duration-300 shadow-lg shadow-indigo-500/30 hover:shadow-indigo-500/50"
|
||||||
|
id="userAvatarBtn"
|
||||||
|
>
|
||||||
|
<span id="userInitials" class="text-xs font-semibold"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="userDropdown"
|
||||||
|
class="hidden absolute right-0 mt-2 w-48 backdrop-blur-xl bg-white/80 rounded-2xl shadow-2xl border border-white/30 py-2 z-50 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="px-4 py-3 border-b border-white/20">
|
||||||
|
<p class="text-xs text-gray-500 font-medium">Signed in as</p>
|
||||||
|
<p class="text-sm font-semibold text-gray-900 truncate mt-0.5" id="userEmail"></p>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="preferences"
|
||||||
|
class="block px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Preferences
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
onclick="logout()"
|
||||||
|
class="w-full text-left px-4 py-2.5 text-sm text-red-600 hover:bg-red-50/50 focus:outline-none transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<!-- Page Header -->
|
||||||
|
<div class="mb-8">
|
||||||
|
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">Preferences</h1>
|
||||||
|
<p class="text-sm sm:text-base text-gray-600">Customize your Job-Pages experience</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Message Container -->
|
||||||
|
<div id="messageContainer" class="mb-4"></div>
|
||||||
|
|
||||||
|
<!-- Preferences Form -->
|
||||||
|
<div class="backdrop-blur-xl bg-white/60 rounded-2xl shadow-xl border border-white/30 p-6">
|
||||||
|
<form id="preferencesForm" class="space-y-6">
|
||||||
|
<!-- Dark Mode Toggle -->
|
||||||
|
<div class="flex items-center justify-between py-4 border-b border-white/20">
|
||||||
|
<div class="flex-1">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 mb-1">Dark Mode</h3>
|
||||||
|
<p class="text-sm text-gray-600">Switch between light and dark themes</p>
|
||||||
|
</div>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="darkModeToggle"
|
||||||
|
class="sr-only peer"
|
||||||
|
>
|
||||||
|
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Default View Preference -->
|
||||||
|
<div class="py-4 border-b border-white/20">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 mb-2">Default View</h3>
|
||||||
|
<p class="text-sm text-gray-600 mb-4">Choose your preferred default view for the jobs page</p>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4">
|
||||||
|
<label class="view-option flex items-center p-4 border-2 border-white/30 backdrop-blur-sm bg-white/40 rounded-2xl cursor-pointer hover:border-indigo-500/50 hover:bg-white/60 transition-all shadow-sm" data-value="table">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="defaultView"
|
||||||
|
value="table"
|
||||||
|
id="viewTable"
|
||||||
|
class="sr-only"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg class="w-6 h-6 text-gray-400 view-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium text-gray-900 view-title">Table View</div>
|
||||||
|
<div class="text-sm text-gray-500">View jobs in a table format</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label class="view-option flex items-center p-4 border-2 border-white/30 backdrop-blur-sm bg-white/40 rounded-2xl cursor-pointer hover:border-indigo-500/50 hover:bg-white/60 transition-all shadow-sm" data-value="card">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="defaultView"
|
||||||
|
value="card"
|
||||||
|
id="viewCard"
|
||||||
|
class="sr-only"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg class="w-6 h-6 text-gray-400 view-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path>
|
||||||
|
</svg>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium text-gray-900 view-title">Card View</div>
|
||||||
|
<div class="text-sm text-gray-500">View jobs in a card format</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Save Button -->
|
||||||
|
<div class="flex justify-end pt-4">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="px-6 py-2.5 backdrop-blur-sm bg-indigo-600/90 text-white rounded-xl font-medium hover:bg-indigo-700/90 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition-all shadow-lg hover:shadow-xl"
|
||||||
|
>
|
||||||
|
Save Preferences
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="js/pocketbase.js"></script>
|
||||||
|
<script src="js/config.js"></script>
|
||||||
|
<script src="js/auth.js"></script>
|
||||||
|
<script src="js/preferences.js"></script>
|
||||||
|
<script>
|
||||||
|
// Check authentication
|
||||||
|
checkAuth();
|
||||||
|
|
||||||
|
// Set user email and initials
|
||||||
|
const user = pb.authStore.model;
|
||||||
|
if (user) {
|
||||||
|
const email = user.email || '';
|
||||||
|
document.getElementById('userEmail').textContent = email;
|
||||||
|
|
||||||
|
// Set initials for avatar
|
||||||
|
const initials = email
|
||||||
|
.split('@')[0]
|
||||||
|
.split('.')
|
||||||
|
.map(part => part.charAt(0).toUpperCase())
|
||||||
|
.join('')
|
||||||
|
.substring(0, 2) || 'U';
|
||||||
|
document.getElementById('userInitials').textContent = initials;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle user dropdown
|
||||||
|
function toggleUserDropdown() {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
dropdown.classList.toggle('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const dropdown = document.getElementById('userDropdown');
|
||||||
|
const button = document.getElementById('userAvatarBtn');
|
||||||
|
if (dropdown && !dropdown.contains(e.target) && !button.contains(e.target)) {
|
||||||
|
dropdown.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make function globally accessible
|
||||||
|
window.toggleUserDropdown = toggleUserDropdown;
|
||||||
|
|
||||||
|
// Load preferences on page load
|
||||||
|
loadPreferences();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user