refactor: convert frontend from submodule to true monorepo

Convert frontend from Git submodule to a regular monorepo directory for simplified development workflow.

Changes:
- Remove frontend submodule tracking (mode 160000 gitlink)
- Add all frontend source files directly to main repository
- Remove frontend/.git directory
- Update CLAUDE.md to clarify true monorepo structure
- Update Frontend Architecture documentation (React Router v6, Socket.IO, Leaflet, ErrorBoundary)

Benefits of Monorepo:
- Single git clone for entire project
- Unified commit history
- Simpler CI/CD pipeline
- Easier for new developers
- No submodule sync issues
- Atomic commits across frontend and backend

Frontend Files Added:
- All React components (MapView, ErrorBoundary, TaskList, SocialFeed, etc.)
- Context providers (AuthContext, SocketContext)
- Complete test suite with MSW
- Dependencies and configuration files

Branch Cleanup:
- Using 'main' as default branch (develop deleted)
- Frontend no longer has separate Git history

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
William Valentin
2025-11-01 11:01:06 -07:00
parent 223dbb14b7
commit 2df5a303ed
38 changed files with 25312 additions and 3 deletions
+396
View File
@@ -0,0 +1,396 @@
import { http, HttpResponse } from 'msw';
// Mock API base URL (matches the proxy in package.json)
const API_URL = 'http://localhost:5000';
// Mock data
const mockUser = {
_id: 'user123',
name: 'Test User',
email: 'test@example.com',
isPremium: false,
points: 100,
adoptedStreets: [],
completedTasks: [],
posts: [],
events: [],
};
const mockStreets = [
{
_id: 'street1',
name: 'Main Street',
city: 'Test City',
state: 'TS',
location: {
type: 'Point',
coordinates: [-73.935242, 40.730610],
},
adoptedBy: 'user123',
status: 'active',
},
{
_id: 'street2',
name: 'Oak Avenue',
city: 'Test City',
state: 'TS',
location: {
type: 'Point',
coordinates: [-73.945242, 40.740610],
},
adoptedBy: 'user456',
status: 'active',
},
];
const mockTasks = [
{
_id: 'task1',
street: 'street1',
description: 'Clean up litter',
type: 'cleaning',
status: 'pending',
createdBy: 'user123',
},
{
_id: 'task2',
street: 'street1',
description: 'Fix pothole',
type: 'repair',
status: 'completed',
createdBy: 'user123',
},
];
const mockPosts = [
{
_id: 'post1',
user: {
_id: 'user123',
name: 'Test User',
profilePicture: null,
},
content: 'Just cleaned up Main Street!',
type: 'text',
likes: [],
comments: [],
createdAt: new Date().toISOString(),
},
{
_id: 'post2',
user: {
_id: 'user456',
name: 'Another User',
profilePicture: null,
},
content: 'Great work everyone!',
type: 'text',
likes: ['user123'],
comments: [],
createdAt: new Date().toISOString(),
},
];
const mockEvents = [
{
_id: 'event1',
title: 'Community Cleanup',
description: 'Annual community cleanup event',
date: new Date(Date.now() + 86400000).toISOString(),
location: 'Central Park',
organizer: 'user123',
participants: [],
},
{
_id: 'event2',
title: 'Tree Planting Day',
description: 'Help us plant trees in the neighborhood',
date: new Date(Date.now() + 172800000).toISOString(),
location: 'Riverside Park',
organizer: 'user456',
participants: ['user123'],
},
];
const mockRewards = [
{
_id: 'reward1',
name: 'Bronze Badge',
description: 'Complete 5 tasks',
pointsCost: 50,
isPremium: false,
},
{
_id: 'reward2',
name: 'Silver Badge',
description: 'Complete 20 tasks',
pointsCost: 150,
isPremium: false,
},
{
_id: 'reward3',
name: 'Premium Badge',
description: 'Exclusive premium badge',
pointsCost: 200,
isPremium: true,
},
];
// Auth handlers
const authHandlers = [
// Register
http.post(`${API_URL}/api/auth/register`, async ({ request }) => {
const body = await request.json();
return HttpResponse.json({
token: 'mock-jwt-token',
});
}),
// Login
http.post(`${API_URL}/api/auth/login`, async ({ request }) => {
const body = await request.json();
if (body.email === 'test@example.com' && body.password === 'password123') {
return HttpResponse.json({
token: 'mock-jwt-token',
});
}
return HttpResponse.json(
{ msg: 'Invalid credentials' },
{ status: 400 }
);
}),
// Get authenticated user
http.get(`${API_URL}/api/auth`, ({ request }) => {
const token = request.headers.get('x-auth-token');
if (!token) {
return HttpResponse.json(
{ msg: 'No token, authorization denied' },
{ status: 401 }
);
}
return HttpResponse.json(mockUser);
}),
];
// Streets handlers
const streetsHandlers = [
// Get all streets
http.get(`${API_URL}/api/streets`, () => {
return HttpResponse.json(mockStreets);
}),
// Adopt a street
http.put(`${API_URL}/api/streets/adopt/:id`, ({ params }) => {
const street = mockStreets.find(s => s._id === params.id);
if (!street) {
return HttpResponse.json(
{ msg: 'Street not found' },
{ status: 404 }
);
}
return HttpResponse.json({
...street,
adoptedBy: 'user123',
});
}),
];
// Tasks handlers
const tasksHandlers = [
// Get all tasks
http.get(`${API_URL}/api/tasks`, () => {
return HttpResponse.json(mockTasks);
}),
// Create a task
http.post(`${API_URL}/api/tasks`, async ({ request }) => {
const body = await request.json();
const newTask = {
_id: `task${Date.now()}`,
...body,
status: 'pending',
createdBy: 'user123',
};
return HttpResponse.json(newTask);
}),
// Complete a task
http.put(`${API_URL}/api/tasks/:id/complete`, ({ params }) => {
const task = mockTasks.find(t => t._id === params.id);
if (!task) {
return HttpResponse.json(
{ msg: 'Task not found' },
{ status: 404 }
);
}
return HttpResponse.json({
...task,
status: 'completed',
});
}),
];
// Posts handlers
const postsHandlers = [
// Get all posts
http.get(`${API_URL}/api/posts`, () => {
return HttpResponse.json(mockPosts);
}),
// Create a post
http.post(`${API_URL}/api/posts`, async ({ request }) => {
const body = await request.json();
const newPost = {
_id: `post${Date.now()}`,
user: {
_id: 'user123',
name: 'Test User',
profilePicture: null,
},
...body,
likes: [],
comments: [],
createdAt: new Date().toISOString(),
};
return HttpResponse.json(newPost);
}),
// Like a post
http.put(`${API_URL}/api/posts/like/:id`, ({ params }) => {
const post = mockPosts.find(p => p._id === params.id);
if (!post) {
return HttpResponse.json(
{ msg: 'Post not found' },
{ status: 404 }
);
}
return HttpResponse.json({
...post,
likes: [...post.likes, 'user123'],
});
}),
];
// Events handlers
const eventsHandlers = [
// Get all events
http.get(`${API_URL}/api/events`, () => {
return HttpResponse.json(mockEvents);
}),
// Create an event
http.post(`${API_URL}/api/events`, async ({ request }) => {
const body = await request.json();
const newEvent = {
_id: `event${Date.now()}`,
...body,
organizer: 'user123',
participants: [],
};
return HttpResponse.json(newEvent);
}),
// RSVP to an event
http.put(`${API_URL}/api/events/rsvp/:id`, ({ params }) => {
const event = mockEvents.find(e => e._id === params.id);
if (!event) {
return HttpResponse.json(
{ msg: 'Event not found' },
{ status: 404 }
);
}
return HttpResponse.json([...event.participants, 'user123']);
}),
];
// Rewards handlers
const rewardsHandlers = [
// Get all rewards
http.get(`${API_URL}/api/rewards`, () => {
return HttpResponse.json(mockRewards);
}),
// Redeem a reward
http.post(`${API_URL}/api/rewards/redeem/:id`, ({ params, request }) => {
const reward = mockRewards.find(r => r._id === params.id);
if (!reward) {
return HttpResponse.json(
{ msg: 'Reward not found' },
{ status: 404 }
);
}
if (mockUser.points < reward.pointsCost) {
return HttpResponse.json(
{ msg: 'Not enough points' },
{ status: 400 }
);
}
if (reward.isPremium && !mockUser.isPremium) {
return HttpResponse.json(
{ msg: 'Premium reward not available' },
{ status: 403 }
);
}
return HttpResponse.json({ msg: 'Reward redeemed successfully' });
}),
];
// Users handlers
const usersHandlers = [
// Get user profile
http.get(`${API_URL}/api/users/:id`, ({ params }) => {
if (params.id === 'user123') {
return HttpResponse.json(mockUser);
}
return HttpResponse.json(
{ msg: 'User not found' },
{ status: 404 }
);
}),
// Update user profile
http.put(`${API_URL}/api/users/:id`, async ({ params, request }) => {
const body = await request.json();
if (params.id === 'user123') {
return HttpResponse.json({
...mockUser,
...body,
});
}
return HttpResponse.json(
{ msg: 'User not found' },
{ status: 404 }
);
}),
];
// Export all handlers
export const handlers = [
...authHandlers,
...streetsHandlers,
...tasksHandlers,
...postsHandlers,
...eventsHandlers,
...rewardsHandlers,
...usersHandlers,
];
+5
View File
@@ -0,0 +1,5 @@
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
// Setup MSW server with default handlers
export const server = setupServer(...handlers);