Fix contact management and import workflow

- Restore single Import button on ContactListPage that navigates to ImportPage
- Add Gmail as third import option on ImportPage (Contacts, Gmail, LinkedIn)
- Fix TypeScript build errors by resolving import conflicts
- Remove unused variables and clean up component exports
- Ensure all builds pass successfully

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
main
Oliver Sylvester-Bradley 2 months ago
parent 451d2a2908
commit 0aa92fdf3a
  1. 24
      public/import-sources.json
  2. 44
      src/components/account/MyCollectionPage.tsx
  3. 2
      src/components/account/MyHomePage.tsx
  4. 25
      src/components/account/PersonhoodCredentials.tsx
  5. 1
      src/components/account/RCardPrivacySettings.tsx
  6. 9
      src/pages/AccountPage.tsx
  7. 40
      src/pages/ContactListPage.tsx
  8. 7
      src/pages/ContactViewPage.tsx
  9. 4
      src/pages/ImportPage.tsx

@ -1,12 +1,4 @@
[
{
"id": "linkedin",
"name": "LinkedIn",
"type": "linkedin",
"icon": "LinkedInIcon",
"description": "Import your LinkedIn connections",
"isAvailable": true
},
{
"id": "contacts",
"name": "Contacts",
@ -14,5 +6,21 @@
"icon": "ContactsIcon",
"description": "Import from your device contacts",
"isAvailable": true
},
{
"id": "gmail",
"name": "Gmail",
"type": "gmail",
"icon": "GmailIcon",
"description": "Import your Gmail contacts",
"isAvailable": true
},
{
"id": "linkedin",
"name": "LinkedIn",
"type": "linkedin",
"icon": "LinkedInIcon",
"description": "Import your LinkedIn connections",
"isAvailable": true
}
]

@ -41,11 +41,7 @@ import {
PostAdd,
Visibility,
FolderOpen,
QueryStats,
Psychology,
Send,
AutoFixHigh,
StarOutline,
AutoAwesome,
} from '@mui/icons-material';
import type { BookmarkedItem, Collection, CollectionFilter, CollectionStats } from '../../types/collection';
@ -67,7 +63,7 @@ const MyCollectionPage = ({}: MyCollectionPageProps) => {
const [menuAnchor, setMenuAnchor] = useState<{ [key: string]: HTMLElement | null }>({});
const [showQueryDialog, setShowQueryDialog] = useState(false);
const [queryText, setQueryText] = useState('');
const [stats, setStats] = useState<CollectionStats>({
const [/* stats */] = useState<CollectionStats>({
totalItems: 0,
unreadItems: 0,
favoriteItems: 0,
@ -231,25 +227,25 @@ const MyCollectionPage = ({}: MyCollectionPageProps) => {
setFilteredItems(mockItems);
// Calculate stats
const categories = [...new Set(mockItems.map(item => item.category).filter(Boolean))];
const types = [...new Set(mockItems.map(item => item.type))];
const recentDate = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7);
const newStats: CollectionStats = {
totalItems: mockItems.length,
unreadItems: mockItems.filter(item => !item.isRead).length,
favoriteItems: mockItems.filter(item => item.isFavorite).length,
byType: types.reduce((acc, type) => ({
...acc,
[type]: mockItems.filter(item => item.type === type).length
}), {}),
byCategory: categories.reduce((acc, category) => ({
...acc,
[category || 'Uncategorized']: mockItems.filter(item => item.category === category).length
}), {}),
recentlyAdded: mockItems.filter(item => item.bookmarkedAt > recentDate).length,
};
setStats(newStats);
// const categories = [...new Set(mockItems.map(item => item.category).filter(Boolean))];
// const types = [...new Set(mockItems.map(item => item.type))];
// const recentDate = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7);
// const newStats: CollectionStats = {
// totalItems: mockItems.length,
// unreadItems: mockItems.filter(item => !item.isRead).length,
// favoriteItems: mockItems.filter(item => item.isFavorite).length,
// byType: types.reduce((acc, type) => ({
// ...acc,
// [type]: mockItems.filter(item => item.type === type).length
// }), {}),
// byCategory: categories.reduce((acc, category) => ({
// ...acc,
// [category || 'Uncategorized']: mockItems.filter(item => item.category === category).length
// }), {}),
// recentlyAdded: mockItems.filter(item => item.bookmarkedAt > recentDate).length,
// };
// setStats(newStats);
}, []);
// Filter and sort items

@ -11,7 +11,6 @@ import {
MenuItem,
TextField,
InputAdornment,
Grid,
Button,
Divider,
} from '@mui/material';
@ -24,7 +23,6 @@ import {
Edit,
Delete,
Share,
ThumbUp,
Comment,
Download,
Launch,

@ -8,7 +8,6 @@ import {
Chip,
Button,
Grid,
LinearProgress,
IconButton,
Dialog,
DialogTitle,
@ -30,17 +29,14 @@ import {
Share,
Timeline,
LocationOn,
CalendarToday,
TrendingUp,
Security,
People,
Star,
Refresh,
Info,
Close,
} from '@mui/icons-material';
import { QRCodeSVG } from 'qrcode.react';
import type { PersonhoodCredentials, PersonhoodVerification } from '../../types/personhood';
import type { PersonhoodCredentials } from '../../types/personhood';
interface PersonhoodCredentialsProps {
credentials: PersonhoodCredentials;
@ -48,7 +44,7 @@ interface PersonhoodCredentialsProps {
onRefreshCredentials: () => void;
}
const PersonhoodCredentials = ({
const PersonhoodCredentialsComponent = ({
credentials,
onGenerateQR,
onRefreshCredentials
@ -57,13 +53,13 @@ const PersonhoodCredentials = ({
const [showQRDialog, setShowQRDialog] = useState(false);
const [showHistoryDialog, setShowHistoryDialog] = useState(false);
const getCredibilityLevel = (score: number) => {
if (score >= 90) return { label: 'Excellent', color: 'success' as const };
if (score >= 75) return { label: 'High', color: 'info' as const };
if (score >= 60) return { label: 'Good', color: 'warning' as const };
if (score >= 40) return { label: 'Fair', color: 'warning' as const };
return { label: 'Low', color: 'error' as const };
};
// const getCredibilityLevel = (score: number) => {
// if (score >= 90) return { label: 'Excellent', color: 'success' as const };
// if (score >= 75) return { label: 'High', color: 'info' as const };
// if (score >= 60) return { label: 'Good', color: 'warning' as const };
// if (score >= 40) return { label: 'Fair', color: 'warning' as const };
// return { label: 'Low', color: 'error' as const };
// };
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat('en-US', {
@ -85,7 +81,6 @@ const PersonhoodCredentials = ({
return `${Math.floor(diffInDays / 365)} years ago`;
};
const credibilityLevel = getCredibilityLevel(credentials.credibilityScore);
return (
<Box>
@ -342,4 +337,4 @@ const PersonhoodCredentials = ({
);
};
export default PersonhoodCredentials;
export default PersonhoodCredentialsComponent;

@ -12,7 +12,6 @@ import {
InputLabel,
Slider,
Divider,
Chip,
alpha,
} from '@mui/material';
import {

@ -12,7 +12,6 @@ import {
Avatar,
Button,
IconButton,
Chip,
useTheme,
alpha,
} from '@mui/material';
@ -37,8 +36,8 @@ import RCardPrivacySettings from '../components/account/RCardPrivacySettings';
import RCardManagement from '../components/account/RCardManagement';
import MyHomePage from '../components/account/MyHomePage';
import MyCollectionPage from '../components/account/MyCollectionPage';
import PersonhoodCredentials from '../components/account/PersonhoodCredentials';
import type { PersonhoodCredentials as PersonhoodCredentialsType } from '../types/personhood';
import PersonhoodCredentialsComponent from '../components/account/PersonhoodCredentials';
import type { PersonhoodCredentials } from '../types/personhood';
interface TabPanelProps {
children?: React.ReactNode;
@ -61,7 +60,7 @@ const AccountPage = () => {
const [selectedRCard, setSelectedRCard] = useState<RCardWithPrivacy | null>(null);
const [showRCardManagement, setShowRCardManagement] = useState(false);
const [editingRCard, setEditingRCard] = useState<RCardWithPrivacy | null>(null);
const [personhoodCredentials, setPersonhoodCredentials] = useState<PersonhoodCredentialsType>({
const [personhoodCredentials] = useState<PersonhoodCredentials>({
userId: 'user-123',
totalVerifications: 12,
uniqueVerifiers: 8,
@ -344,7 +343,7 @@ const AccountPage = () => {
{/* Personhood Credentials Section */}
<Box sx={{ mt: 4 }}>
<PersonhoodCredentials
<PersonhoodCredentialsComponent
credentials={personhoodCredentials}
onGenerateQR={handleGenerateQR}
onRefreshCredentials={handleRefreshCredentials}

@ -5,7 +5,6 @@ import {
Box,
Tabs,
Tab,
Avatar,
Chip,
TextField,
InputAdornment,
@ -30,8 +29,7 @@ import {
Group,
CloudDownload,
VerifiedUser,
Favorite,
MailOutline
Favorite
} from '@mui/icons-material';
import { dataService } from '../services/dataService';
import type { Contact } from '../types/contact';
@ -115,45 +113,13 @@ const ContactListPage = () => {
</Typography>
</Box>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
<Button
variant="outlined"
startIcon={<MailOutline />}
onClick={() => navigate('/import/gmail')}
sx={{
borderRadius: 2,
color: '#EA4335',
borderColor: '#EA4335',
'&:hover': {
borderColor: '#EA4335',
backgroundColor: alpha('#EA4335', 0.04)
}
}}
>
Import Gmail
</Button>
<Button
variant="outlined"
startIcon={<LinkedIn />}
onClick={() => navigate('/import/linkedin')}
sx={{
borderRadius: 2,
color: '#0077b5',
borderColor: '#0077b5',
'&:hover': {
borderColor: '#0077b5',
backgroundColor: alpha('#0077b5', 0.04)
}
}}
>
Import LinkedIn
</Button>
<Button
variant="outlined"
startIcon={<CloudDownload />}
onClick={() => navigate('/import/contacts')}
onClick={() => navigate('/import')}
sx={{ borderRadius: 2 }}
>
Import Contacts
Import
</Button>
<Button
variant="outlined"

@ -4,12 +4,10 @@ import {
Typography,
Box,
Paper,
Avatar,
Button,
Chip,
Divider,
Grid,
IconButton,
Card,
CardContent,
Alert,
@ -23,16 +21,11 @@ import {
Phone,
Business,
LinkedIn,
Edit,
Share,
Delete,
Person,
Schedule,
Source,
Group,
Add,
ThumbUp,
Star,
Send,
VerifiedUser,
Favorite

@ -15,7 +15,7 @@ import {
Box,
Alert,
} from '@mui/material';
import { LinkedIn, Contacts, CloudDownload } from '@mui/icons-material';
import { LinkedIn, Contacts, CloudDownload, MailOutline } from '@mui/icons-material';
import { dataService } from '../services/dataService';
import type { ImportSource } from '../types/contact';
@ -78,6 +78,8 @@ const ImportPage = () => {
return <LinkedIn sx={{ fontSize: 40, color: '#0077b5' }} />;
case 'ContactsIcon':
return <Contacts sx={{ fontSize: 40, color: '#4caf50' }} />;
case 'GmailIcon':
return <MailOutline sx={{ fontSize: 40, color: '#EA4335' }} />;
default:
return <CloudDownload sx={{ fontSize: 40 }} />;
}

Loading…
Cancel
Save