feat: add config API and update ApkList to use configured tabs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
8
app/api/config/route.ts
Normal file
8
app/api/config/route.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { APK_TABS } from '@/lib/constants';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
return NextResponse.json({
|
||||||
|
tabs: APK_TABS,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { type ApkEnvironment } from '@/lib/apk-parser';
|
import { type ApkEnvironment } from '@/lib/apk-parser';
|
||||||
import ApkTable from './ApkTable';
|
import ApkTable from './ApkTable';
|
||||||
@@ -22,16 +22,26 @@ interface ApkListProps {
|
|||||||
commitId: string;
|
commitId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TABS: ApkEnvironment[] = ['dev', 'product', 'sandbox', 'other'];
|
|
||||||
|
|
||||||
export default function ApkList({ project, version, commitId }: ApkListProps) {
|
export default function ApkList({ project, version, commitId }: ApkListProps) {
|
||||||
|
const [configuredTabs, setConfiguredTabs] = useState<string[]>([]);
|
||||||
const [activeTab, setActiveTab] = useState<ApkEnvironment>('product');
|
const [activeTab, setActiveTab] = useState<ApkEnvironment>('product');
|
||||||
|
|
||||||
|
// Fetch config on mount
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/config')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setConfiguredTabs(data.tabs))
|
||||||
|
.catch(() => setConfiguredTabs(['dev', 'sandbox'])); // fallback
|
||||||
|
}, []);
|
||||||
|
|
||||||
const { data: apks, isLoading, error } = useSWR<Apk[]>(
|
const { data: apks, isLoading, error } = useSWR<Apk[]>(
|
||||||
`/api/apks?project=${project}&version=${version}&commit=${commitId}`,
|
`/api/apks?project=${project}&version=${version}&commit=${commitId}`,
|
||||||
fetcher
|
fetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Build tabs: product -> configured -> other
|
||||||
|
const TABS: ApkEnvironment[] = ['product', ...configuredTabs.filter(t => t !== 'product' && t !== 'other') as ApkEnvironment[], 'other'];
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="text-center py-8 text-gray-500">
|
<div className="text-center py-8 text-gray-500">
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||||
import ApkList from '../ApkList';
|
import ApkList from '../ApkList';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
jest.mock('swr');
|
jest.mock('swr');
|
||||||
|
|
||||||
|
// Mock fetch for config API
|
||||||
|
const mockFetch = jest.fn();
|
||||||
|
global.fetch = mockFetch;
|
||||||
|
|
||||||
describe('ApkList', () => {
|
describe('ApkList', () => {
|
||||||
const mockApks = [
|
const mockApks = [
|
||||||
{
|
{
|
||||||
@@ -39,20 +43,34 @@ describe('ApkList', () => {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
|
// Mock config API response
|
||||||
|
mockFetch.mockResolvedValue({
|
||||||
|
json: () => Promise.resolve({ tabs: ['dev', 'sandbox'] }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render all 4 tabs', () => {
|
afterEach(() => {
|
||||||
|
mockFetch.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render all 4 tabs', async () => {
|
||||||
render(<ApkList project="FT" version="0_42" commitId="" />);
|
render(<ApkList project="FT" version="0_42" commitId="" />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.getByText(/dev/)).toBeInTheDocument();
|
expect(screen.getByText(/dev/)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/product/)).toBeInTheDocument();
|
expect(screen.getByText(/product/)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/sandbox/)).toBeInTheDocument();
|
expect(screen.getByText(/sandbox/)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/other/)).toBeInTheDocument();
|
expect(screen.getByText(/other/)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should switch tabs correctly', () => {
|
it('should switch tabs correctly', async () => {
|
||||||
render(<ApkList project="FT" version="0_42" commitId="" />);
|
render(<ApkList project="FT" version="0_42" commitId="" />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/dev/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
const devTab = screen.getByText(/dev/);
|
const devTab = screen.getByText(/dev/);
|
||||||
fireEvent.click(devTab);
|
fireEvent.click(devTab);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user