workspace — VS Code
index.ts
api/client.ts
utils/format.ts
README.md
1// UserDashboard — main entry point
2import{ useState, useEffect, useCallback }from'react';
3import{ fetchData, postData }from'./api/client';
4import{ formatDate, formatCurrency }from'./utils/format';
5import type{ UserProfile, ApiResponse }from'./types';
6
7constBASE_URL='https://api.example.com/v2';
8constTIMEOUT_MS=5000;
9
10interfaceDashboardState{
11user:UserProfile|null;
12loading:boolean;
13error:string|null;
14lastUpdated:Date|null;
15}
16
17export constUserDashboard= () => {
18const[state,setState] =useState<DashboardState>({
19user:null,
20loading:true,
21error:null,
22lastUpdated:null,
23});
24
25constloadUser=useCallback(async() => {
26setState(prev=> ({ ...prev,loading:true, error:null}));
27try{
28constres:ApiResponse<UserProfile> =awaitfetchData(`${BASE_URL}/profile`);
29if(!res.ok)throw newError(res.message);
30setState(prev=> ({
31...prev,
32user:res.data,
33loading:false,
34lastUpdated:newDate(),
35}));
36}catch(err) {
37setState(prev=> ({ ...prev,loading:false,error:'Failed to load profile'}));
38console.error('[UserDashboard]',err);
39}
40}, []);
41
42useEffect(() => {
43loadUser();
44constinterval=setInterval(loadUser,TIMEOUT_MS*12);
45return() =>clearInterval(interval);
46}, [loadUser]);
47
48consthandleUpdate=async(patch:Partial<UserProfile>) => {
49awaitpostData(`${BASE_URL}/profile`,patch);
50loadUser();
51};
52
53if(state.loading)return<Spinner/>;
54if(state.error)return<ErrorBannermessage={state.error} />;
55
56return(
57<Dashboard
58user={state.user}
59onUpdate={handleUpdate}
60lastUpdated={formatDate(state.lastUpdated)}
61/>
62);
63};
64
65export defaultUserDashboard;
TypeScriptUTF-8Ln 63, Col 2Spaces: 2
preview.ts
no preview
1// preview renderer
2export constrender= (id:string) => {
3return<Previewsrc={id} />;
4};