feat(FRE-596): add PWA setup and responsive design

- Register service worker for offline caching (app shell + API responses)
- Link manifest.json in index.html with updated theme colors
- Update manifest start_url to /app/dashboard for PWA experience
- Add comprehensive team management CSS with responsive breakpoints
- Add alert, loading, and danger button styles
- Mobile-first responsive layout for team list and detail views
This commit is contained in:
Senior Engineer
2026-04-28 01:48:48 -04:00
committed by Michael Freno
parent b6d1f4c3b6
commit 88f0239ab7
5 changed files with 709 additions and 7 deletions

View File

@@ -4,6 +4,14 @@ import { ClerkProvider } from './lib/auth/clerk-provider';
import { routes } from './routes';
import './index.css';
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').catch((err) => {
console.warn('Service worker registration failed:', err);
});
});
}
render(
() => (
<ClerkProvider>

View File

@@ -101,3 +101,625 @@ input:focus, textarea:focus, select:focus {
--sidebar-width: 0px;
}
}
/* Waitlist Page Styles */
.waitlist-page {
min-height: 100vh;
background: var(--color-bg-primary);
}
.waitlist-hero {
min-height: calc(100vh - 80px);
display: flex;
flex-direction: column;
padding: 40px 20px;
max-width: 1200px;
margin: 0 auto;
}
.waitlist-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 40px;
}
.waitlist-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
font-size: 0.875rem;
color: var(--color-text-secondary);
}
.badge-dot {
width: 8px;
height: 8px;
background: var(--color-accent);
border-radius: 50%;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.waitlist-headline {
font-size: 2.75rem;
font-weight: 700;
line-height: 1.2;
background: linear-gradient(135deg, #fff 0%, #a3a3a3 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 16px;
}
.waitlist-subheadline {
font-size: 1.25rem;
color: var(--color-text-secondary);
max-width: 600px;
line-height: 1.6;
}
.waitlist-form {
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 32px;
margin: 24px 0;
}
.waitlist-form .form-group {
margin-bottom: 20px;
}
.waitlist-form label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--color-text-primary);
font-size: 0.9375rem;
}
.waitlist-form .optional {
color: var(--color-text-muted);
font-size: 0.8125rem;
margin-left: 4px;
}
.waitlist-form input {
width: 100%;
padding: 14px 16px;
font-size: 1rem;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
color: var(--color-text-primary);
transition: all var(--transition-fast);
}
.waitlist-form input:focus {
border-color: var(--color-accent);
outline: none;
box-shadow: 0 0 0 3px var(--color-accent-muted);
}
.waitlist-form input::placeholder {
color: var(--color-text-muted);
}
.submit-btn {
width: 100%;
padding: 16px;
font-size: 1.125rem;
font-weight: 600;
color: white;
background: var(--color-accent);
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
}
.submit-btn:hover:not(:disabled) {
background: var(--color-accent-hover);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.submit-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.error-message {
background: rgba(239, 68, 68, 0.1);
border: 1px solid var(--color-error);
color: var(--color-error);
padding: 12px 16px;
border-radius: var(--radius-md);
font-size: 0.9375rem;
margin-top: 16px;
}
.privacy-note {
text-align: center;
color: var(--color-text-muted);
font-size: 0.875rem;
margin-top: 20px;
}
.waitlist-count {
text-align: center;
color: var(--color-text-muted);
font-size: 0.875rem;
margin-top: 16px;
}
.waitlist-footer {
padding: 24px 0;
border-top: 1px solid var(--color-border);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
color: var(--color-text-muted);
}
.footer-links {
display: flex;
gap: 20px;
}
.footer-links a {
color: var(--color-text-secondary);
}
.footer-links a:hover {
color: var(--color-accent);
}
.waitlist-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 24px;
margin-top: 32px;
}
.waitlist-features .feature {
text-align: center;
padding: 24px;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
transition: all var(--transition-fast);
}
.waitlist-features .feature:hover {
border-color: var(--color-border-hover);
transform: translateY(-2px);
}
.waitlist-features .feature-icon {
font-size: 2rem;
margin-bottom: 12px;
}
.waitlist-features .feature h3 {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text-primary);
margin-bottom: 8px;
}
.waitlist-features .feature p {
color: var(--color-text-secondary);
font-size: 0.9375rem;
line-height: 1.5;
}
.waitlist-social-proof {
margin-top: 48px;
padding: 32px;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
}
.waitlist-social-proof .social-proof-header h3 {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text-primary);
text-align: center;
margin-bottom: 24px;
}
.testimonials {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
}
.testimonial {
padding: 20px;
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
}
.testimonial-quote {
font-size: 0.9375rem;
color: var(--color-text-secondary);
font-style: italic;
margin-bottom: 16px;
line-height: 1.6;
}
.testimonial-author {
display: flex;
flex-direction: column;
gap: 4px;
}
.testimonial-author .author-name {
font-weight: 600;
color: var(--color-text-primary);
font-size: 0.9375rem;
}
.testimonial-author .author-role {
font-size: 0.8125rem;
color: var(--color-text-muted);
}
.waitlist-success {
text-align: center;
padding: 24px 0;
}
.waitlist-success h2 {
font-size: 1.5rem;
color: var(--color-success);
margin-bottom: 12px;
}
.waitlist-success p {
color: var(--color-text-secondary);
margin-bottom: 24px;
}
.referral-info {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 20px;
margin-top: 20px;
}
.referral-label {
font-size: 0.875rem;
color: var(--color-text-muted);
margin-bottom: 8px;
}
.referral-code {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-bottom: 12px;
}
.code-display {
font-family: 'Monaco', 'Menlo', monospace;
font-size: 1.25rem;
font-weight: 700;
background: var(--color-bg-tertiary);
padding: 12px 20px;
border-radius: var(--radius-md);
letter-spacing: 2px;
color: var(--color-accent);
}
.copy-btn {
padding: 10px 16px;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
}
.copy-btn:hover {
background: var(--color-bg-elevated);
border-color: var(--color-border-hover);
}
.referral-hint {
font-size: 0.8125rem;
color: var(--color-text-muted);
text-align: center;
}
/* Team Management */
.freno-teams {
max-width: 1200px;
margin: 0 auto;
padding: 24px;
}
.freno-team-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
margin-top: 24px;
}
.freno-team-card {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
transition: all var(--transition-fast);
position: relative;
}
.freno-team-card:hover {
border-color: var(--color-border-hover);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.freno-team-card-new {
border-style: dashed;
align-items: center;
justify-content: center;
min-height: 140px;
cursor: pointer;
text-align: center;
}
.freno-team-card-new:hover {
border-color: var(--color-accent);
background: var(--color-accent-muted);
}
.freno-team-card-link {
text-decoration: none;
color: inherit;
}
.freno-team-icon {
font-size: 2rem;
margin-bottom: 4px;
}
.freno-team-card h3 {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text-primary);
}
.freno-team-card-actions {
display: flex;
justify-content: flex-end;
margin-top: 8px;
padding-top: 12px;
border-top: 1px solid var(--color-border);
}
/* Team Detail */
.freno-team-detail {
max-width: 900px;
margin: 0 auto;
padding: 24px;
}
.freno-back-link {
font-size: 0.875rem;
color: var(--color-text-secondary);
margin-bottom: 8px;
display: inline-block;
}
.freno-back-link:hover {
color: var(--color-accent);
}
.freno-team-meta {
color: var(--color-text-muted);
font-size: 0.875rem;
margin-top: 4px;
}
.freno-header-actions {
display: flex;
gap: 8px;
}
.freno-team-members-section {
margin-top: 32px;
}
.freno-team-members-section h2 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 16px;
}
.freno-members-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.freno-member-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
}
.freno-member-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.freno-member-id {
font-weight: 500;
color: var(--color-text-primary);
}
.freno-member-joined {
font-size: 0.8125rem;
color: var(--color-text-muted);
}
.freno-member-actions {
display: flex;
gap: 8px;
align-items: center;
}
.freno-select {
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 6px 12px;
color: var(--color-text-primary);
font-size: 0.875rem;
cursor: pointer;
}
.freno-select:focus {
border-color: var(--color-accent);
outline: none;
}
/* Alert */
.freno-alert {
padding: 12px 16px;
border-radius: var(--radius-md);
margin-bottom: 16px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
}
.freno-alert-error {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
color: #fca5a5;
}
.freno-alert-dismiss {
background: none;
border: none;
color: inherit;
cursor: pointer;
font-size: 1.125rem;
padding: 0 4px;
opacity: 0.7;
}
.freno-alert-dismiss:hover {
opacity: 1;
}
/* Danger button */
.freno-btn-danger {
background: var(--color-error);
color: white;
padding: 8px 16px;
border-radius: var(--radius-md);
font-size: 0.875rem;
font-weight: 500;
transition: opacity var(--transition-fast);
}
.freno-btn-danger:hover {
opacity: 0.9;
}
.freno-btn-danger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.freno-btn-danger-sm {
color: var(--color-error);
font-size: 0.8125rem;
padding: 4px 8px;
border-radius: var(--radius-sm);
transition: background var(--transition-fast);
}
.freno-btn-danger-sm:hover {
background: rgba(239, 68, 68, 0.1);
}
/* Loading state */
.freno-loading {
text-align: center;
padding: 48px;
color: var(--color-text-muted);
}
/* Responsive adjustments */
@media (max-width: 768px) {
.freno-teams,
.freno-team-detail {
padding: 16px;
}
.freno-team-grid {
grid-template-columns: 1fr;
}
.freno-page-header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.freno-header-actions {
width: 100%;
flex-direction: column;
}
.freno-header-actions button {
width: 100%;
}
.freno-member-row {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.freno-member-actions {
width: 100%;
justify-content: flex-end;
}
}