mobile_responsiveness_adjustments_tables_grids_and_sidebars
This commit is contained in:
parent
dd4ffff3cc
commit
7bd5f9f0e4
@ -2,7 +2,7 @@ import { Outlet, Link } from '@tanstack/react-router';
|
|||||||
import { useAuthStore } from '../../hooks/use-auth';
|
import { useAuthStore } from '../../hooks/use-auth';
|
||||||
import { useThemeStore } from '../../hooks/use-theme';
|
import { useThemeStore } from '../../hooks/use-theme';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { LayoutDashboard, FolderKanban, FileSignature, Users, LogOut, Hexagon, Sun, Moon } from 'lucide-react';
|
import { LayoutDashboard, FolderKanban, FileSignature, Users, LogOut, Sun, Moon } from 'lucide-react';
|
||||||
|
|
||||||
export const MainLayout = () => {
|
export const MainLayout = () => {
|
||||||
const { isAuthenticated, logout, user } = useAuthStore();
|
const { isAuthenticated, logout, user } = useAuthStore();
|
||||||
|
|||||||
@ -152,8 +152,8 @@ export const AssetManagement: React.FC = () => {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-ink-100 bg-ink-50 text-xs font-bold uppercase tracking-wider text-ink-600">
|
<tr className="border-b border-ink-100 bg-ink-50 text-xs font-bold uppercase tracking-wider text-ink-600">
|
||||||
<th className="px-5 py-3.5">Asset Title / Category</th>
|
<th className="px-5 py-3.5">Asset Title / Category</th>
|
||||||
<th className="px-5 py-3.5">Tags</th>
|
<th className="px-5 py-3.5 hidden md:table-cell">Tags</th>
|
||||||
<th className="px-5 py-3.5">Downloads</th>
|
<th className="px-5 py-3.5 hidden sm:table-cell">Downloads</th>
|
||||||
<th className="px-5 py-3.5">Status</th>
|
<th className="px-5 py-3.5">Status</th>
|
||||||
<th className="px-5 py-3.5 text-right">Actions</th>
|
<th className="px-5 py-3.5 text-right">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -184,7 +184,7 @@ export const AssetManagement: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-5 py-4">
|
<td className="px-5 py-4 hidden md:table-cell">
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{asset.tags.map(t => (
|
{asset.tags.map(t => (
|
||||||
<span key={t} className="rounded bg-ink-50 px-2 py-0.5 text-[10px] font-semibold text-ink-600 border border-ink-100">
|
<span key={t} className="rounded bg-ink-50 px-2 py-0.5 text-[10px] font-semibold text-ink-600 border border-ink-100">
|
||||||
@ -193,7 +193,7 @@ export const AssetManagement: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-5 py-4 font-mono font-semibold text-ink-700">
|
<td className="px-5 py-4 font-mono font-semibold text-ink-700 hidden sm:table-cell">
|
||||||
{asset.downloadsCount}
|
{asset.downloadsCount}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-5 py-4">
|
<td className="px-5 py-4">
|
||||||
|
|||||||
1
Channel-Frontend/src/fontsource.d.ts
vendored
Normal file
1
Channel-Frontend/src/fontsource.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
declare module '@fontsource-variable/inter';
|
||||||
@ -2,13 +2,12 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Shield, CheckCircle, AlertCircle, ChevronRight, KeyRound, Eye, EyeOff } from 'lucide-react';
|
import { Shield, CheckCircle, AlertCircle, ChevronRight, KeyRound, Eye, EyeOff } from 'lucide-react';
|
||||||
import axios from 'axios';
|
|
||||||
import { axiosInstance } from '../services/axios';
|
import { axiosInstance } from '../services/axios';
|
||||||
import { useAuthStore } from '../hooks/use-auth';
|
import { useAuthStore } from '../hooks/use-auth';
|
||||||
import { useToast } from '../hooks/use-toast';
|
import { useToast } from '../hooks/use-toast';
|
||||||
|
|
||||||
export const InvitePage: React.FC = () => {
|
export const InvitePage: React.FC = () => {
|
||||||
const { success, error: toastError } = useToast();
|
const { success } = useToast();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const token = searchParams.get('token');
|
const token = searchParams.get('token');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -29,9 +28,10 @@ export const InvitePage: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateToken = async () => {
|
// verify token on mount
|
||||||
|
const verifyToken = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.get(`/auth/invite/${token}`);
|
const response = await axiosInstance.get(`/auth/verify-invite?token=${token}`);
|
||||||
setEmail(response.data.email);
|
setEmail(response.data.email);
|
||||||
setStatus('valid');
|
setStatus('valid');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -39,7 +39,7 @@ export const InvitePage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
validateToken();
|
verifyToken();
|
||||||
}, [token]);
|
}, [token]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
@ -51,25 +51,36 @@ export const InvitePage: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.length < 6) {
|
if (password.length < 8) {
|
||||||
setError('Password must be at least 6 characters');
|
setError('Password must be at least 8 characters long');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.post('/auth/invite/accept', {
|
const response = await axiosInstance.post('/auth/activate-partner', {
|
||||||
token,
|
token,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|
||||||
setAuth(response.data);
|
// Show success toast
|
||||||
success("Account created successfully", "Please complete your partner onboarding profile.");
|
success('Account activated successfully!');
|
||||||
|
|
||||||
|
// Set auth store
|
||||||
|
setAuth({ user: response.data.user, accessToken: response.data.token });
|
||||||
|
|
||||||
|
// Redirect based on role / status
|
||||||
|
setTimeout(() => {
|
||||||
|
if (response.data.user.role === 'ADMIN') {
|
||||||
|
navigate('/admin');
|
||||||
|
} else if (response.data.user.onboardingStatus === 'APPROVED') {
|
||||||
|
navigate('/client');
|
||||||
|
} else {
|
||||||
navigate('/onboarding');
|
navigate('/onboarding');
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const errMsg = err.response?.data?.error || 'Failed to accept invite';
|
setError(err.response?.data?.detail || 'Activation failed');
|
||||||
setError(errMsg);
|
|
||||||
toastError("Failed to accept invite", errMsg);
|
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -77,7 +88,7 @@ export const InvitePage: React.FC = () => {
|
|||||||
if (status === 'loading') {
|
if (status === 'loading') {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-ink-50 flex items-center justify-center">
|
<div className="min-h-screen bg-ink-50 flex items-center justify-center">
|
||||||
<div className="w-8 h-8 border-4 border-ink-900/30 border-t-ink-900 rounded-full animate-spin" />
|
<div className="w-8 h-8 border-4 border-ink-900/35 border-t-ink-900 rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -85,7 +96,7 @@ export const InvitePage: React.FC = () => {
|
|||||||
if (status === 'invalid') {
|
if (status === 'invalid') {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-ink-50 flex items-center justify-center p-4">
|
<div className="min-h-screen bg-ink-50 flex items-center justify-center p-4">
|
||||||
<div className="w-full max-w-md bg-ink-0 rounded-3xl p-8 border border-ink-200 text-center shadow-2xl">
|
<div className="w-full max-w-md bg-ink-0 rounded-[2rem] p-8 border border-ink-200 shadow-2xl text-center">
|
||||||
<div className="w-16 h-16 bg-red-500/10 rounded-full flex items-center justify-center mx-auto mb-6">
|
<div className="w-16 h-16 bg-red-500/10 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||||
<AlertCircle className="w-8 h-8 text-red-600 dark:text-red-500" />
|
<AlertCircle className="w-8 h-8 text-red-600 dark:text-red-500" />
|
||||||
</div>
|
</div>
|
||||||
@ -112,7 +123,9 @@ export const InvitePage: React.FC = () => {
|
|||||||
<Shield className="w-6 h-6 text-ink-0" />
|
<Shield className="w-6 h-6 text-ink-0" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl login-title font-extrabold tracking-tight mb-2">Welcome to Tech4Biz</h1>
|
<h1 className="text-3xl login-title font-extrabold tracking-tight mb-2">Welcome to Tech4Biz</h1>
|
||||||
<p className="text-sm login-desc text-ink-500">Please set up your password to activate your channel partner account.</p>
|
<p className="text-sm login-desc text-ink-500">
|
||||||
|
Set up your partner account for <span className="text-ink-900 font-bold">{email}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { loginUser } from "../services/auth-api";
|
|||||||
import { useAuthStore } from "../hooks/use-auth";
|
import { useAuthStore } from "../hooks/use-auth";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Hexagon, Lock, Mail, ArrowRight, Eye, EyeOff } from "lucide-react";
|
import { Lock, Mail, ArrowRight, Eye, EyeOff } from "lucide-react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useToast } from "../hooks/use-toast";
|
import { useToast } from "../hooks/use-toast";
|
||||||
|
|
||||||
|
|||||||
@ -341,8 +341,8 @@ export const DirectoryPage: React.FC = () => {
|
|||||||
<tr>
|
<tr>
|
||||||
<th className="px-5 py-3">Partner</th>
|
<th className="px-5 py-3">Partner</th>
|
||||||
<th className="px-5 py-3">Status</th>
|
<th className="px-5 py-3">Status</th>
|
||||||
<th className="px-5 py-3">MFA</th>
|
<th className="px-5 py-3 hidden sm:table-cell">MFA</th>
|
||||||
<th className="px-5 py-3">Joined</th>
|
<th className="px-5 py-3 hidden sm:table-cell">Joined</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-ink-200 bg-ink-0">
|
<tbody className="divide-y divide-ink-200 bg-ink-0">
|
||||||
@ -391,7 +391,7 @@ export const DirectoryPage: React.FC = () => {
|
|||||||
{sc.label}
|
{sc.label}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-5 py-4">
|
<td className="px-5 py-4 hidden sm:table-cell">
|
||||||
{partner.mfaEnabled ? (
|
{partner.mfaEnabled ? (
|
||||||
<span className="text-xs font-bold text-ink-900">
|
<span className="text-xs font-bold text-ink-900">
|
||||||
Enabled
|
Enabled
|
||||||
@ -402,7 +402,7 @@ export const DirectoryPage: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-5 py-4 text-xs text-ink-500 font-medium">
|
<td className="px-5 py-4 text-xs text-ink-500 font-medium hidden sm:table-cell">
|
||||||
{new Date(partner.createdAt).toLocaleDateString()}
|
{new Date(partner.createdAt).toLocaleDateString()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user