feat: add submission status filter to browse teams page
- Add has_submission filter option to useTeams hook - Add Submission Status dropdown filter (All/Submitted/Not Submitted) - Filter persists in URL query params 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
417ff3390b
commit
b2a28c6fd3
@@ -15,7 +15,8 @@
|
|||||||
"Bash(git add:*)",
|
"Bash(git add:*)",
|
||||||
"Bash(git commit:*)",
|
"Bash(git commit:*)",
|
||||||
"Bash(git push)",
|
"Bash(git push)",
|
||||||
"Bash(findstr:*)"
|
"Bash(findstr:*)",
|
||||||
|
"Bash(ls:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"nx-mcp": {
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["nx", "mcp"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!-- nx configuration start-->
|
||||||
|
<!-- Leave the start & end comments to automatically receive updates. -->
|
||||||
|
|
||||||
|
# General Guidelines for working with Nx
|
||||||
|
|
||||||
|
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
|
||||||
|
- You have access to the Nx MCP server and its tools, use them to help the user
|
||||||
|
- When answering questions about the repository, use the `nx_workspace` tool first to gain an understanding of the workspace architecture where applicable.
|
||||||
|
- When working in individual projects, use the `nx_project_details` mcp tool to analyze and understand the specific project structure and dependencies
|
||||||
|
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
|
||||||
|
- If the user needs help with an Nx configuration or project graph error, use the `nx_workspace` tool to get any errors
|
||||||
|
|
||||||
|
<!-- nx configuration end-->
|
||||||
@@ -31,6 +31,13 @@ const MEMBER_FILTER_OPTIONS = [
|
|||||||
{ label: '5 Members (Full)', value: '5', minMembers: 5, maxMembers: 5 },
|
{ label: '5 Members (Full)', value: '5', minMembers: 5, maxMembers: 5 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Submission status filter options
|
||||||
|
const SUBMISSION_FILTER_OPTIONS = [
|
||||||
|
{ label: 'All Teams', value: '' },
|
||||||
|
{ label: 'Submitted', value: 'true' },
|
||||||
|
{ label: 'Not Submitted', value: 'false' },
|
||||||
|
];
|
||||||
|
|
||||||
// Skeleton card component for loading state
|
// Skeleton card component for loading state
|
||||||
const TeamCardSkeleton: FC = () => (
|
const TeamCardSkeleton: FC = () => (
|
||||||
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-md overflow-hidden flex flex-col border dark:border-gray-800 animate-pulse">
|
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-md overflow-hidden flex flex-col border dark:border-gray-800 animate-pulse">
|
||||||
@@ -72,11 +79,13 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
const initialSearch = searchParams.get('search') || '';
|
const initialSearch = searchParams.get('search') || '';
|
||||||
const initialCity = searchParams.get('city') || '';
|
const initialCity = searchParams.get('city') || '';
|
||||||
const initialMembers = searchParams.get('members') || '';
|
const initialMembers = searchParams.get('members') || '';
|
||||||
|
const initialSubmission = searchParams.get('submission') || '';
|
||||||
|
|
||||||
const [searchTerm, setSearchTerm] = useState(initialSearch);
|
const [searchTerm, setSearchTerm] = useState(initialSearch);
|
||||||
const [debouncedSearch, setDebouncedSearch] = useState(initialSearch);
|
const [debouncedSearch, setDebouncedSearch] = useState(initialSearch);
|
||||||
const [selectedCity, setSelectedCity] = useState(initialCity);
|
const [selectedCity, setSelectedCity] = useState(initialCity);
|
||||||
const [selectedMembers, setSelectedMembers] = useState(initialMembers);
|
const [selectedMembers, setSelectedMembers] = useState(initialMembers);
|
||||||
|
const [selectedSubmission, setSelectedSubmission] = useState(initialSubmission);
|
||||||
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
||||||
const [showJoinModal, setShowJoinModal] = useState(false);
|
const [showJoinModal, setShowJoinModal] = useState(false);
|
||||||
const [currentPage, setCurrentPage] = useState(
|
const [currentPage, setCurrentPage] = useState(
|
||||||
@@ -94,6 +103,7 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
search?: string;
|
search?: string;
|
||||||
city?: string;
|
city?: string;
|
||||||
members?: string;
|
members?: string;
|
||||||
|
submission?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const newParams = new URLSearchParams(searchParams);
|
const newParams = new URLSearchParams(searchParams);
|
||||||
|
|
||||||
@@ -137,6 +147,14 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.submission !== undefined) {
|
||||||
|
if (params.submission === '') {
|
||||||
|
newParams.delete('submission');
|
||||||
|
} else {
|
||||||
|
newParams.set('submission', params.submission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setSearchParams(newParams, { replace: true });
|
setSearchParams(newParams, { replace: true });
|
||||||
},
|
},
|
||||||
[searchParams, setSearchParams]
|
[searchParams, setSearchParams]
|
||||||
@@ -167,6 +185,9 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
(opt) => opt.value === selectedMembers
|
(opt) => opt.value === selectedMembers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Convert submission filter value to boolean
|
||||||
|
const hasSubmissionFilter = selectedSubmission === 'true' ? true : selectedSubmission === 'false' ? false : undefined;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: teamsData,
|
data: teamsData,
|
||||||
isLoading,
|
isLoading,
|
||||||
@@ -179,6 +200,7 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
visibility: ETeamVisibility.PUBLIC,
|
visibility: ETeamVisibility.PUBLIC,
|
||||||
minMembers: memberFilter?.minMembers,
|
minMembers: memberFilter?.minMembers,
|
||||||
maxMembers: memberFilter?.maxMembers,
|
maxMembers: memberFilter?.maxMembers,
|
||||||
|
hasSubmission: hasSubmissionFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: myTeamsData } = useMyTeams();
|
const { data: myTeamsData } = useMyTeams();
|
||||||
@@ -278,7 +300,7 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
{/* Filters */}
|
{/* Filters */}
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||||
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg shadow-sm mb-6 border dark:border-gray-800">
|
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg shadow-sm mb-6 border dark:border-gray-800">
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
Search Teams
|
Search Teams
|
||||||
@@ -321,6 +343,26 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
|
Submission Status
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedSubmission}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelectedSubmission(e.target.value);
|
||||||
|
setCurrentPage(1);
|
||||||
|
updateUrlParams({ submission: e.target.value, page: 1 });
|
||||||
|
}}
|
||||||
|
className="w-full h-[42px] px-3 border border-gray-300 dark:border-gray-700 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||||
|
>
|
||||||
|
{SUBMISSION_FILTER_OPTIONS.map((option) => (
|
||||||
|
<option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export const useTeams = (params?: {
|
|||||||
search?: string;
|
search?: string;
|
||||||
minMembers?: number;
|
minMembers?: number;
|
||||||
maxMembers?: number;
|
maxMembers?: number;
|
||||||
|
hasSubmission?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: teamKeys.list(params),
|
queryKey: teamKeys.list(params),
|
||||||
@@ -139,6 +140,7 @@ export const useTeams = (params?: {
|
|||||||
if (params?.visibility) queryParams.append('visibility', params.visibility);
|
if (params?.visibility) queryParams.append('visibility', params.visibility);
|
||||||
if (params?.minMembers) queryParams.append('min_members', String(params.minMembers));
|
if (params?.minMembers) queryParams.append('min_members', String(params.minMembers));
|
||||||
if (params?.maxMembers) queryParams.append('max_members', String(params.maxMembers));
|
if (params?.maxMembers) queryParams.append('max_members', String(params.maxMembers));
|
||||||
|
if (params?.hasSubmission !== undefined) queryParams.append('has_submission', String(params.hasSubmission));
|
||||||
|
|
||||||
const response = await hackathonApi.get<ListResponseWithMeta<Team>>(
|
const response = await hackathonApi.get<ListResponseWithMeta<Team>>(
|
||||||
`/teams/browse${queryParams.toString() ? `?${queryParams.toString()}` : ''}`
|
`/teams/browse${queryParams.toString() ? `?${queryParams.toString()}` : ''}`
|
||||||
|
|||||||
Reference in New Issue
Block a user