335 lines
8.3 KiB
TypeScript
335 lines
8.3 KiB
TypeScript
/**
|
|
* In-memory team data for alpha prototype
|
|
*/
|
|
|
|
export type TeamRole = 'owner' | 'manager' | 'driver';
|
|
export type TeamMembershipStatus = 'active' | 'pending' | 'none';
|
|
|
|
export interface Team {
|
|
id: string;
|
|
name: string;
|
|
tag: string;
|
|
description: string;
|
|
ownerId: string;
|
|
leagues: string[];
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface TeamMembership {
|
|
teamId: string;
|
|
driverId: string;
|
|
role: TeamRole;
|
|
status: TeamMembershipStatus;
|
|
joinedAt: Date;
|
|
}
|
|
|
|
export interface TeamJoinRequest {
|
|
id: string;
|
|
teamId: string;
|
|
driverId: string;
|
|
requestedAt: Date;
|
|
message?: string;
|
|
}
|
|
|
|
// In-memory storage
|
|
let teams: Team[] = [];
|
|
let teamMemberships: TeamMembership[] = [];
|
|
let teamJoinRequests: TeamJoinRequest[] = [];
|
|
|
|
// Current driver ID (matches di-container)
|
|
const CURRENT_DRIVER_ID = 'driver-1';
|
|
|
|
// Initialize with seed data
|
|
export function initializeTeamData() {
|
|
teams = [
|
|
{
|
|
id: 'team-1',
|
|
name: 'Apex Racing',
|
|
tag: 'APEX',
|
|
description: 'Professional GT3 racing team competing at the highest level',
|
|
ownerId: CURRENT_DRIVER_ID,
|
|
leagues: ['league-1'],
|
|
createdAt: new Date('2024-01-20'),
|
|
},
|
|
{
|
|
id: 'team-2',
|
|
name: 'Speed Demons',
|
|
tag: 'SPDM',
|
|
description: 'Fast and furious racing with a competitive edge',
|
|
ownerId: 'driver-2',
|
|
leagues: ['league-1'],
|
|
createdAt: new Date('2024-02-01'),
|
|
},
|
|
{
|
|
id: 'team-3',
|
|
name: 'Weekend Warriors',
|
|
tag: 'WKND',
|
|
description: 'Casual but competitive weekend racing',
|
|
ownerId: 'driver-3',
|
|
leagues: ['league-1'],
|
|
createdAt: new Date('2024-02-10'),
|
|
},
|
|
];
|
|
|
|
teamMemberships = [
|
|
{
|
|
teamId: 'team-1',
|
|
driverId: CURRENT_DRIVER_ID,
|
|
role: 'owner',
|
|
status: 'active',
|
|
joinedAt: new Date('2024-01-20'),
|
|
},
|
|
{
|
|
teamId: 'team-2',
|
|
driverId: 'driver-2',
|
|
role: 'owner',
|
|
status: 'active',
|
|
joinedAt: new Date('2024-02-01'),
|
|
},
|
|
{
|
|
teamId: 'team-3',
|
|
driverId: 'driver-3',
|
|
role: 'owner',
|
|
status: 'active',
|
|
joinedAt: new Date('2024-02-10'),
|
|
},
|
|
];
|
|
|
|
teamJoinRequests = [];
|
|
}
|
|
|
|
// Get all teams
|
|
export function getAllTeams(): Team[] {
|
|
return teams;
|
|
}
|
|
|
|
// Get team by ID
|
|
export function getTeam(teamId: string): Team | null {
|
|
return teams.find(t => t.id === teamId) || null;
|
|
}
|
|
|
|
// Get team membership for a driver
|
|
export function getTeamMembership(teamId: string, driverId: string): TeamMembership | null {
|
|
return teamMemberships.find(m => m.teamId === teamId && m.driverId === driverId) || null;
|
|
}
|
|
|
|
// Get driver's team
|
|
export function getDriverTeam(driverId: string): { team: Team; membership: TeamMembership } | null {
|
|
const membership = teamMemberships.find(m => m.driverId === driverId && m.status === 'active');
|
|
if (!membership) return null;
|
|
|
|
const team = getTeam(membership.teamId);
|
|
if (!team) return null;
|
|
|
|
return { team, membership };
|
|
}
|
|
|
|
// Get all members for a team
|
|
export function getTeamMembers(teamId: string): TeamMembership[] {
|
|
return teamMemberships.filter(m => m.teamId === teamId && m.status === 'active');
|
|
}
|
|
|
|
// Get pending join requests for a team
|
|
export function getTeamJoinRequests(teamId: string): TeamJoinRequest[] {
|
|
return teamJoinRequests.filter(r => r.teamId === teamId);
|
|
}
|
|
|
|
// Create a new team
|
|
export function createTeam(
|
|
name: string,
|
|
tag: string,
|
|
description: string,
|
|
ownerId: string,
|
|
leagues: string[]
|
|
): Team {
|
|
// Check if driver already has a team
|
|
const existingTeam = getDriverTeam(ownerId);
|
|
if (existingTeam) {
|
|
throw new Error('Driver already belongs to a team');
|
|
}
|
|
|
|
const team: Team = {
|
|
id: `team-${Date.now()}`,
|
|
name,
|
|
tag,
|
|
description,
|
|
ownerId,
|
|
leagues,
|
|
createdAt: new Date(),
|
|
};
|
|
|
|
teams.push(team);
|
|
|
|
// Auto-assign creator as owner
|
|
teamMemberships.push({
|
|
teamId: team.id,
|
|
driverId: ownerId,
|
|
role: 'owner',
|
|
status: 'active',
|
|
joinedAt: new Date(),
|
|
});
|
|
|
|
return team;
|
|
}
|
|
|
|
// Join a team
|
|
export function joinTeam(teamId: string, driverId: string): void {
|
|
const existingTeam = getDriverTeam(driverId);
|
|
if (existingTeam) {
|
|
throw new Error('Driver already belongs to a team');
|
|
}
|
|
|
|
const existing = getTeamMembership(teamId, driverId);
|
|
if (existing) {
|
|
throw new Error('Already a member or have a pending request');
|
|
}
|
|
|
|
teamMemberships.push({
|
|
teamId,
|
|
driverId,
|
|
role: 'driver',
|
|
status: 'active',
|
|
joinedAt: new Date(),
|
|
});
|
|
}
|
|
|
|
// Request to join a team
|
|
export function requestToJoinTeam(teamId: string, driverId: string, message?: string): void {
|
|
const existingTeam = getDriverTeam(driverId);
|
|
if (existingTeam) {
|
|
throw new Error('Driver already belongs to a team');
|
|
}
|
|
|
|
const existing = getTeamMembership(teamId, driverId);
|
|
if (existing) {
|
|
throw new Error('Already a member or have a pending request');
|
|
}
|
|
|
|
const existingRequest = teamJoinRequests.find(r => r.teamId === teamId && r.driverId === driverId);
|
|
if (existingRequest) {
|
|
throw new Error('Join request already pending');
|
|
}
|
|
|
|
teamJoinRequests.push({
|
|
id: `team-request-${Date.now()}`,
|
|
teamId,
|
|
driverId,
|
|
requestedAt: new Date(),
|
|
message,
|
|
});
|
|
}
|
|
|
|
// Leave a team
|
|
export function leaveTeam(teamId: string, driverId: string): void {
|
|
const membership = getTeamMembership(teamId, driverId);
|
|
if (!membership) {
|
|
throw new Error('Not a member of this team');
|
|
}
|
|
|
|
if (membership.role === 'owner') {
|
|
throw new Error('Team owner cannot leave. Transfer ownership or disband team first.');
|
|
}
|
|
|
|
teamMemberships = teamMemberships.filter(m => !(m.teamId === teamId && m.driverId === driverId));
|
|
}
|
|
|
|
// Approve join request
|
|
export function approveTeamJoinRequest(requestId: string): void {
|
|
const request = teamJoinRequests.find(r => r.id === requestId);
|
|
if (!request) {
|
|
throw new Error('Join request not found');
|
|
}
|
|
|
|
teamMemberships.push({
|
|
teamId: request.teamId,
|
|
driverId: request.driverId,
|
|
role: 'driver',
|
|
status: 'active',
|
|
joinedAt: new Date(),
|
|
});
|
|
|
|
teamJoinRequests = teamJoinRequests.filter(r => r.id !== requestId);
|
|
}
|
|
|
|
// Reject join request
|
|
export function rejectTeamJoinRequest(requestId: string): void {
|
|
teamJoinRequests = teamJoinRequests.filter(r => r.id !== requestId);
|
|
}
|
|
|
|
// Remove member (admin action)
|
|
export function removeTeamMember(teamId: string, driverId: string, removedBy: string): void {
|
|
const removerMembership = getTeamMembership(teamId, removedBy);
|
|
if (!removerMembership || (removerMembership.role !== 'owner' && removerMembership.role !== 'manager')) {
|
|
throw new Error('Only owners and managers can remove members');
|
|
}
|
|
|
|
const targetMembership = getTeamMembership(teamId, driverId);
|
|
if (!targetMembership) {
|
|
throw new Error('Member not found');
|
|
}
|
|
|
|
if (targetMembership.role === 'owner') {
|
|
throw new Error('Cannot remove team owner');
|
|
}
|
|
|
|
teamMemberships = teamMemberships.filter(m => !(m.teamId === teamId && m.driverId === driverId));
|
|
}
|
|
|
|
// Update member role
|
|
export function updateTeamMemberRole(
|
|
teamId: string,
|
|
driverId: string,
|
|
newRole: TeamRole,
|
|
updatedBy: string
|
|
): void {
|
|
const updaterMembership = getTeamMembership(teamId, updatedBy);
|
|
if (!updaterMembership || updaterMembership.role !== 'owner') {
|
|
throw new Error('Only team owner can change roles');
|
|
}
|
|
|
|
const targetMembership = getTeamMembership(teamId, driverId);
|
|
if (!targetMembership) {
|
|
throw new Error('Member not found');
|
|
}
|
|
|
|
if (newRole === 'owner') {
|
|
throw new Error('Use transfer ownership to change owner');
|
|
}
|
|
|
|
teamMemberships = teamMemberships.map(m =>
|
|
m.teamId === teamId && m.driverId === driverId
|
|
? { ...m, role: newRole }
|
|
: m
|
|
);
|
|
}
|
|
|
|
// Check if driver is owner or manager
|
|
export function isTeamOwnerOrManager(teamId: string, driverId: string): boolean {
|
|
const membership = getTeamMembership(teamId, driverId);
|
|
return membership?.role === 'owner' || membership?.role === 'manager';
|
|
}
|
|
|
|
// Get current driver ID
|
|
export function getCurrentDriverId(): string {
|
|
return CURRENT_DRIVER_ID;
|
|
}
|
|
|
|
// Update team info
|
|
export function updateTeam(
|
|
teamId: string,
|
|
updates: Partial<Pick<Team, 'name' | 'tag' | 'description' | 'leagues'>>,
|
|
updatedBy: string
|
|
): void {
|
|
if (!isTeamOwnerOrManager(teamId, updatedBy)) {
|
|
throw new Error('Only owners and managers can update team info');
|
|
}
|
|
|
|
teams = teams.map(t =>
|
|
t.id === teamId
|
|
? { ...t, ...updates }
|
|
: t
|
|
);
|
|
}
|
|
|
|
// Initialize on module load
|
|
initializeTeamData(); |