refactor(frontend): replace status string literals with constants
- Create constants/statuses.js with MATCH_STATUS, SUGGESTION_STATUS, MATCH_FILTER - Update MatchCard, MatchesPage, HistoryPage, RatePartnerPage to use MATCH_STATUS - Update RecordingTab to use SUGGESTION_STATUS - Update Navbar to use MATCH_STATUS for API calls
This commit is contained in:
@@ -5,6 +5,7 @@ import Avatar from '../common/Avatar';
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { matchesAPI } from '../../services/api';
|
import { matchesAPI } from '../../services/api';
|
||||||
import { connectSocket, disconnectSocket, getSocket } from '../../services/socket';
|
import { connectSocket, disconnectSocket, getSocket } from '../../services/socket';
|
||||||
|
import { MATCH_STATUS } from '../../constants';
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const { user, logout } = useAuth();
|
const { user, logout } = useAuth();
|
||||||
@@ -48,7 +49,7 @@ const Navbar = () => {
|
|||||||
|
|
||||||
const loadPendingMatches = async () => {
|
const loadPendingMatches = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await matchesAPI.getMatches(null, 'pending');
|
const result = await matchesAPI.getMatches(null, MATCH_STATUS.PENDING);
|
||||||
// Only count incoming requests (where user is not the initiator)
|
// Only count incoming requests (where user is not the initiator)
|
||||||
const incomingCount = (result.data || []).filter(m => !m.isInitiator).length;
|
const incomingCount = (result.data || []).filter(m => !m.isInitiator).length;
|
||||||
setPendingMatchesCount(incomingCount);
|
setPendingMatchesCount(incomingCount);
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { MessageCircle, Check, X, Loader2, Calendar, MapPin } from 'lucide-react';
|
import { MessageCircle, Check, X, Loader2, Calendar, MapPin } from 'lucide-react';
|
||||||
|
import { MATCH_STATUS } from '../../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Match card for matches list page
|
* Match card for matches list page
|
||||||
* Shows match details with accept/reject/chat actions
|
* Shows match details with accept/reject/chat actions
|
||||||
*/
|
*/
|
||||||
const MatchCard = ({ match, onAccept, onReject, onOpenChat, processing }) => {
|
const MatchCard = ({ match, onAccept, onReject, onOpenChat, processing }) => {
|
||||||
const isIncoming = !match.isInitiator && match.status === 'pending';
|
const isIncoming = !match.isInitiator && match.status === MATCH_STATUS.PENDING;
|
||||||
const isOutgoing = match.isInitiator && match.status === 'pending';
|
const isOutgoing = match.isInitiator && match.status === MATCH_STATUS.PENDING;
|
||||||
const isAccepted = match.status === 'accepted';
|
const isAccepted = match.status === MATCH_STATUS.ACCEPTED;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 hover:shadow-md transition-shadow">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 hover:shadow-md transition-shadow">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { Video, VideoOff, Clock, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react';
|
import { Video, VideoOff, Clock, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react';
|
||||||
import { matchingAPI } from '../../services/api';
|
import { matchingAPI } from '../../services/api';
|
||||||
import Avatar from '../common/Avatar';
|
import Avatar from '../common/Avatar';
|
||||||
|
import { SUGGESTION_STATUS } from '../../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RecordingTab - Main component for managing recording partnerships
|
* RecordingTab - Main component for managing recording partnerships
|
||||||
@@ -199,8 +200,8 @@ const RecordingTab = ({ slug, event, myHeats }) => {
|
|||||||
key={suggestion.id || suggestion.heat?.id}
|
key={suggestion.id || suggestion.heat?.id}
|
||||||
suggestion={suggestion}
|
suggestion={suggestion}
|
||||||
type="toBeRecorded"
|
type="toBeRecorded"
|
||||||
onAccept={() => handleUpdateStatus(suggestion.id, 'accepted')}
|
onAccept={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)}
|
||||||
onReject={() => handleUpdateStatus(suggestion.id, 'rejected')}
|
onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -225,8 +226,8 @@ const RecordingTab = ({ slug, event, myHeats }) => {
|
|||||||
key={suggestion.id || suggestion.heat?.id}
|
key={suggestion.id || suggestion.heat?.id}
|
||||||
suggestion={suggestion}
|
suggestion={suggestion}
|
||||||
type="toRecord"
|
type="toRecord"
|
||||||
onAccept={() => handleUpdateStatus(suggestion.id, 'accepted')}
|
onAccept={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)}
|
||||||
onReject={() => handleUpdateStatus(suggestion.id, 'rejected')}
|
onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -278,21 +279,21 @@ const SuggestionCard = ({ suggestion, type, onAccept, onReject }) => {
|
|||||||
// Status badge
|
// Status badge
|
||||||
const getStatusBadge = () => {
|
const getStatusBadge = () => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'accepted':
|
case SUGGESTION_STATUS.ACCEPTED:
|
||||||
return (
|
return (
|
||||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 rounded">
|
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 rounded">
|
||||||
<CheckCircle className="w-3 h-3" />
|
<CheckCircle className="w-3 h-3" />
|
||||||
Zaakceptowano
|
Zaakceptowano
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
case 'rejected':
|
case SUGGESTION_STATUS.REJECTED:
|
||||||
return (
|
return (
|
||||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-red-100 text-red-800 rounded">
|
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-red-100 text-red-800 rounded">
|
||||||
<XCircle className="w-3 h-3" />
|
<XCircle className="w-3 h-3" />
|
||||||
Odrzucono
|
Odrzucono
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
case 'not_found':
|
case SUGGESTION_STATUS.NOT_FOUND:
|
||||||
return (
|
return (
|
||||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-amber-100 text-amber-800 rounded">
|
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-amber-100 text-amber-800 rounded">
|
||||||
<AlertTriangle className="w-3 h-3" />
|
<AlertTriangle className="w-3 h-3" />
|
||||||
@@ -305,7 +306,7 @@ const SuggestionCard = ({ suggestion, type, onAccept, onReject }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// No recorder found
|
// No recorder found
|
||||||
if (status === 'not_found') {
|
if (status === SUGGESTION_STATUS.NOT_FOUND) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between p-3 bg-amber-50 border border-amber-200 rounded-lg">
|
<div className="flex items-center justify-between p-3 bg-amber-50 border border-amber-200 rounded-lg">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -346,7 +347,7 @@ const SuggestionCard = ({ suggestion, type, onAccept, onReject }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{status === 'pending' ? (
|
{status === SUGGESTION_STATUS.PENDING ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={onAccept}
|
onClick={onAccept}
|
||||||
|
|||||||
1
frontend/src/constants/index.js
Normal file
1
frontend/src/constants/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { MATCH_STATUS, SUGGESTION_STATUS, MATCH_FILTER } from './statuses';
|
||||||
28
frontend/src/constants/statuses.js
Normal file
28
frontend/src/constants/statuses.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Match status constants
|
||||||
|
*/
|
||||||
|
export const MATCH_STATUS = {
|
||||||
|
PENDING: 'pending',
|
||||||
|
ACCEPTED: 'accepted',
|
||||||
|
REJECTED: 'rejected',
|
||||||
|
COMPLETED: 'completed',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggestion status constants (for auto-matching)
|
||||||
|
*/
|
||||||
|
export const SUGGESTION_STATUS = {
|
||||||
|
PENDING: 'pending',
|
||||||
|
ACCEPTED: 'accepted',
|
||||||
|
REJECTED: 'rejected',
|
||||||
|
NOT_FOUND: 'not_found',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter options for matches list
|
||||||
|
*/
|
||||||
|
export const MATCH_FILTER = {
|
||||||
|
ALL: 'all',
|
||||||
|
PENDING: 'pending',
|
||||||
|
ACCEPTED: 'accepted',
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import Layout from '../components/layout/Layout';
|
import Layout from '../components/layout/Layout';
|
||||||
import { mockMatches, mockRatings } from '../mocks/matches';
|
import { mockMatches, mockRatings } from '../mocks/matches';
|
||||||
import { Calendar, MapPin, Star, MessageCircle } from 'lucide-react';
|
import { Calendar, MapPin, Star, MessageCircle } from 'lucide-react';
|
||||||
|
import { MATCH_STATUS } from '../constants';
|
||||||
|
|
||||||
const HistoryPage = () => {
|
const HistoryPage = () => {
|
||||||
return (
|
return (
|
||||||
@@ -42,12 +43,12 @@ const HistoryPage = () => {
|
|||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
className={`px-3 py-1 rounded-full text-sm font-medium ${
|
className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||||
match.status === 'completed'
|
match.status === MATCH_STATUS.COMPLETED
|
||||||
? 'bg-green-100 text-green-800'
|
? 'bg-green-100 text-green-800'
|
||||||
: 'bg-blue-100 text-blue-800'
|
: 'bg-blue-100 text-blue-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{match.status === 'completed' ? 'Completed' : 'Active'}
|
{match.status === MATCH_STATUS.COMPLETED ? 'Completed' : 'Active'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ import { matchesAPI } from '../services/api';
|
|||||||
import { Loader2, Users } from 'lucide-react';
|
import { Loader2, Users } from 'lucide-react';
|
||||||
import { connectSocket, disconnectSocket, getSocket } from '../services/socket';
|
import { connectSocket, disconnectSocket, getSocket } from '../services/socket';
|
||||||
import { MatchCard } from '../components/matches';
|
import { MatchCard } from '../components/matches';
|
||||||
|
import { MATCH_STATUS, MATCH_FILTER } from '../constants';
|
||||||
|
|
||||||
const MatchesPage = () => {
|
const MatchesPage = () => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [matches, setMatches] = useState([]);
|
const [matches, setMatches] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [filter, setFilter] = useState('all'); // 'all', 'pending', 'accepted'
|
const [filter, setFilter] = useState(MATCH_FILTER.ALL);
|
||||||
const [processingMatchId, setProcessingMatchId] = useState(null);
|
const [processingMatchId, setProcessingMatchId] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -108,20 +109,20 @@ const MatchesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenChat = (match) => {
|
const handleOpenChat = (match) => {
|
||||||
if (match.status === 'accepted' && match.roomId) {
|
if (match.status === MATCH_STATUS.ACCEPTED && match.roomId) {
|
||||||
navigate(`/matches/${match.slug}/chat`);
|
navigate(`/matches/${match.slug}/chat`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter matches based on selected filter
|
// Filter matches based on selected filter
|
||||||
const filteredMatches = matches.filter(match => {
|
const filteredMatches = matches.filter(match => {
|
||||||
if (filter === 'all') return true;
|
if (filter === MATCH_FILTER.ALL) return true;
|
||||||
return match.status === filter;
|
return match.status === filter;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Separate pending incoming matches (where user is recipient)
|
// Separate pending incoming matches (where user is recipient)
|
||||||
const pendingIncoming = filteredMatches.filter(m => m.status === 'pending' && !m.isInitiator);
|
const pendingIncoming = filteredMatches.filter(m => m.status === MATCH_STATUS.PENDING && !m.isInitiator);
|
||||||
const otherMatches = filteredMatches.filter(m => !(m.status === 'pending' && !m.isInitiator));
|
const otherMatches = filteredMatches.filter(m => !(m.status === MATCH_STATUS.PENDING && !m.isInitiator));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
@@ -136,9 +137,9 @@ const MatchesPage = () => {
|
|||||||
{/* Filter Tabs */}
|
{/* Filter Tabs */}
|
||||||
<div className="bg-white rounded-lg shadow-sm p-1 mb-6 flex gap-1">
|
<div className="bg-white rounded-lg shadow-sm p-1 mb-6 flex gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter('all')}
|
onClick={() => setFilter(MATCH_FILTER.ALL)}
|
||||||
className={`flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
className={`flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||||
filter === 'all'
|
filter === MATCH_FILTER.ALL
|
||||||
? 'bg-primary-600 text-white'
|
? 'bg-primary-600 text-white'
|
||||||
: 'text-gray-600 hover:bg-gray-100'
|
: 'text-gray-600 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
@@ -146,24 +147,24 @@ const MatchesPage = () => {
|
|||||||
All ({matches.length})
|
All ({matches.length})
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter('pending')}
|
onClick={() => setFilter(MATCH_FILTER.PENDING)}
|
||||||
className={`flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
className={`flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||||
filter === 'pending'
|
filter === MATCH_FILTER.PENDING
|
||||||
? 'bg-primary-600 text-white'
|
? 'bg-primary-600 text-white'
|
||||||
: 'text-gray-600 hover:bg-gray-100'
|
: 'text-gray-600 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Pending ({matches.filter(m => m.status === 'pending').length})
|
Pending ({matches.filter(m => m.status === MATCH_STATUS.PENDING).length})
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter('accepted')}
|
onClick={() => setFilter(MATCH_FILTER.ACCEPTED)}
|
||||||
className={`flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
className={`flex-1 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||||
filter === 'accepted'
|
filter === MATCH_FILTER.ACCEPTED
|
||||||
? 'bg-primary-600 text-white'
|
? 'bg-primary-600 text-white'
|
||||||
: 'text-gray-600 hover:bg-gray-100'
|
: 'text-gray-600 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Active ({matches.filter(m => m.status === 'accepted').length})
|
Active ({matches.filter(m => m.status === MATCH_STATUS.ACCEPTED).length})
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom';
|
|||||||
import Layout from '../components/layout/Layout';
|
import Layout from '../components/layout/Layout';
|
||||||
import { matchesAPI } from '../services/api';
|
import { matchesAPI } from '../services/api';
|
||||||
import { Star, Loader2 } from 'lucide-react';
|
import { Star, Loader2 } from 'lucide-react';
|
||||||
|
import { MATCH_STATUS } from '../constants';
|
||||||
|
|
||||||
const RatePartnerPage = () => {
|
const RatePartnerPage = () => {
|
||||||
const { slug } = useParams();
|
const { slug } = useParams();
|
||||||
@@ -24,7 +25,7 @@ const RatePartnerPage = () => {
|
|||||||
setMatch(result.data);
|
setMatch(result.data);
|
||||||
|
|
||||||
// Check if this match can be rated
|
// Check if this match can be rated
|
||||||
if (result.data.status !== 'accepted' && result.data.status !== 'completed') {
|
if (result.data.status !== MATCH_STATUS.ACCEPTED && result.data.status !== MATCH_STATUS.COMPLETED) {
|
||||||
alert('This match must be accepted before rating.');
|
alert('This match must be accepted before rating.');
|
||||||
navigate('/matches');
|
navigate('/matches');
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user