iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Modern Web

TypeScript 啟動!系列 第 24

[Day 24] TypeScript 零售業者模擬演練 II

  • 分享至 

  • xImage
  •  

今天繼續來完成前端網站 React 的部分,首先有好幾個地方要修正,像是資料呈現的屬性資料顯然是不夠的、圖表上的顯然是庫存而不是價格、每週的銷售紀錄等等呈現。

Step1. 修正 types

// types.tsx

export type SaleRecord = {
    week: number;
    price: number;       // 價錢
    initialStock: number; // 存貨,初始的存貨量
    sales: number;      // 銷售,賣出的數量
    remainingStock: number; // 剩餘存貨 = 初始存貨 - 賣出的數量
    revenue: number;    // 營收 = 價錢 x 賣出的數量
    accumulatedRevenue: number; // 累積營收
  };

修正需要呈現的資料屬性。

Step2. 修正 LineChart.tsx

// components/LineChart.tsx

import React from 'react';
import { Line } from 'react-chartjs-2';
import { SaleRecord } from './types';
import { Chart, registerables } from 'chart.js';

type Props = {
    data: SaleRecord[];
};

const LineChart: React.FC<Props> = ({ data }) => {
  Chart.register(...registerables);
    const chartData = {
        labels: data.map(record => `Week ${record.week}`),
        datasets: [
            {
                label: 'Remaining Stock',
                data: data.map(record => record.remainingStock),
                fill: false,
                backgroundColor: 'rgb(75, 192, 192)',
                borderColor: 'rgba(75, 192, 192, 0.2)',
            },
        ],
    };

    return <Line data={chartData} />;
};

export default LineChart;

大致上修改一下呈現的是剩餘存貨量~

Step3. 修正銷售價格(PricingControl.tsx)

// components/PricingControl.tsx

import React, { useState } from 'react';

type Props = {
    onSelect: (price: number) => void;
};

const PricingControl: React.FC<Props> = ({ onSelect }) => {
    const [selectedPrice, setSelectedPrice] = useState<number | null>(null);
    const prices = [60, 54, 48, 36];

    return (
        <div>
            {prices.map((price) => (
                <button
                    key={price}
                    onClick={() => {
                        setSelectedPrice(price);
                        onSelect(price);
                    }}
                    disabled={selectedPrice !== null && price > selectedPrice}
                >
                    ${price}
                </button>
            ))}
        </div>
    );
};

export default PricingControl;

大概修正一下銷售數字和銷售一但低價賣出,就不能回到高價賣出了。

Step4. salesLogic.tsx

// logic/salesLogic.ts

export const simulateSales = (remainingStock: number): number => {
    const sales = Math.floor(Math.random() * (120 - 70 + 1) + 70);
    return Math.min(sales, remainingStock);
}

修正一下銷售的隨機,讓他先介於70~120之間,並且不能超過剩餘存貨數量。

Step5. 表單呈現(SalesTable.tsx)

// components/SalesTable.tsx
import React from 'react';
import { SaleRecord } from './types';

type Props = {
    data: SaleRecord[];
};

const SalesTable: React.FC<Props> = ({ data }) => {
    return (
        <table>
            <thead>
                <tr>
                    <th>週</th>
                    <th>價格</th>
                    <th>存貨</th>
                    <th>銷售</th>
                    <th>剩餘存貨</th>
                    <th>營收</th>
                    <th>累計營收</th>
                </tr>
            </thead>
            <tbody>
                {data.map(record => (
                    <tr key={record.week}>
                        <td>{record.week}</td>
                        <td>{record.price}</td>
                        <td>{record.initialStock}</td>
                        <td>{record.sales}</td>
                        <td>{record.remainingStock}</td>
                        <td>{record.revenue}</td>
                        <td>{record.accumulatedRevenue}</td>
                    </tr>
                ))}
            </tbody>
        </table>
    );
};

export default SalesTable;

先製作一個簡單的表格呈現,之後再美化。

Step6. 修正頁面呈現(App.tsx)

// App.tsx

import React, { useState } from 'react';
import LineChart from './Component/LineChart';
import PricingControl from './Component/PricingControl';
import { SaleRecord } from './Component/types';
import { simulateSales } from './Component/salesLogic';
import SalesTable from './Component/SalesTable';

const INITIAL_STOCK = 2000;

const App: React.FC = () => {
    const [salesData, setSalesData] = useState<SaleRecord[]>([
        { week: 1, price: 60, initialStock: INITIAL_STOCK, sales: 0, remainingStock: INITIAL_STOCK, revenue: 0, accumulatedRevenue: 0 },
    ]);
    const handlePricingSelect = (price: number) => {
        const currentWeek = salesData.length;
        const previousRecord = salesData[currentWeek - 1];
        const sales = simulateSales(previousRecord.remainingStock);
        const newRemainingStock = previousRecord.remainingStock - sales;
        const revenue = sales * price;
        const accumulatedRevenue = previousRecord.accumulatedRevenue + revenue;

        const record: SaleRecord = {
            week: currentWeek + 1,
            price,
            initialStock: INITIAL_STOCK,
            sales,
            remainingStock: newRemainingStock,
            revenue,
            accumulatedRevenue,
        };

        setSalesData([...salesData, record]);
    };

    return (
        <div className="App">
            <h1>Retail Simulation</h1>
            <PricingControl onSelect={handlePricingSelect} />
            <LineChart data={salesData} />
            <SalesTable data={salesData} />
        </div>
    );
};

export default App;

其中針對 handlePricingSelect 做了點修正,並且新增 <SalesTable data={salesData} /> 的呈現。

今天就先到這裡好了,明天在繼續優化一下。


上一篇
[Day 23] TypeScript 零售業者模擬演練
下一篇
[Day 25] TypeScript 零售業者模擬演練 III
系列文
TypeScript 啟動!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言