|
|
@@ -1,6 +1,20 @@
|
|
|
import { useState, useEffect } from "react";
|
|
|
-import { Button, TextInput, Spinner } from "@contentful/f36-components";
|
|
|
-import { contentModelApi, type ContentModel } from "../../services/modules/contentModel";
|
|
|
+import {
|
|
|
+ Button,
|
|
|
+ TextInput,
|
|
|
+ Spinner,
|
|
|
+ Flex,
|
|
|
+ Box,
|
|
|
+ Heading,
|
|
|
+ Text,
|
|
|
+ Table,
|
|
|
+ IconButton,
|
|
|
+} from "@contentful/f36-components";
|
|
|
+import { QuestionIcon } from "@contentful/f36-icons";
|
|
|
+import {
|
|
|
+ contentModelApi,
|
|
|
+ type ContentModel,
|
|
|
+} from "../../services/modules/contentModel";
|
|
|
import styles from "./index.module.css";
|
|
|
|
|
|
export default function ContentModelPage() {
|
|
|
@@ -40,89 +54,117 @@ export default function ContentModelPage() {
|
|
|
};
|
|
|
|
|
|
return (
|
|
|
- <div className={styles.page}>
|
|
|
- {/* 页面头部 */}
|
|
|
- <div className={styles.pageHeader}>
|
|
|
- <div className={styles.pageHeaderLeft}>
|
|
|
- <h1 className={styles.pageTitle}>Content model</h1>
|
|
|
- <span className={styles.helpIcon}>?</span>
|
|
|
- </div>
|
|
|
- <div className={styles.pageHeaderRight}>
|
|
|
+ <Box padding="spacingXl" className={styles.page}>
|
|
|
+ {/* 页面头部 - 标题、搜索框、按钮在同一行 */}
|
|
|
+ <Flex
|
|
|
+ alignItems="center"
|
|
|
+ justifyContent="center"
|
|
|
+ gap="spacingM"
|
|
|
+ marginBottom="spacingL"
|
|
|
+ >
|
|
|
+ {/* 标题和帮助图标 */}
|
|
|
+ <Flex alignItems="center" gap="spacingXs" style={{ flexShrink: 0 }}>
|
|
|
+ <Heading as="h1" marginBottom="none">
|
|
|
+ Content model
|
|
|
+ </Heading>
|
|
|
+ <IconButton
|
|
|
+ variant="transparent"
|
|
|
+ size="small"
|
|
|
+ icon={<QuestionIcon />}
|
|
|
+ aria-label="帮助"
|
|
|
+ />
|
|
|
+ </Flex>
|
|
|
+
|
|
|
+ {/* 搜索框 - 自适应剩余空间 */}
|
|
|
+ <Box style={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
|
|
|
+ <TextInput
|
|
|
+ placeholder="Search for a content type"
|
|
|
+ value={searchText}
|
|
|
+ size='small'
|
|
|
+ onChange={(e) => handleSearch(e.target.value)}
|
|
|
+ className={styles.searchInput}
|
|
|
+ />
|
|
|
+ </Box>
|
|
|
+
|
|
|
+ {/* 右侧按钮 */}
|
|
|
+ <Flex gap="spacingS" style={{ flexShrink: 0 }}>
|
|
|
<Button variant="secondary" size="small">
|
|
|
Visual Modeler
|
|
|
</Button>
|
|
|
<Button variant="primary" size="small" onClick={handleCreate}>
|
|
|
+ Create content type
|
|
|
</Button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 搜索栏 */}
|
|
|
- <div className={styles.searchBar}>
|
|
|
- <TextInput
|
|
|
- placeholder="Search for a content type"
|
|
|
- value={searchText}
|
|
|
- onChange={(e) => handleSearch(e.target.value)}
|
|
|
- />
|
|
|
- </div>
|
|
|
+ </Flex>
|
|
|
+ </Flex>
|
|
|
|
|
|
{/* 表格 */}
|
|
|
- <div className={styles.tableContainer}>
|
|
|
+ <Box className={styles.tableContainer}>
|
|
|
{loading ? (
|
|
|
- <div className={styles.loadingContainer}>
|
|
|
+ <Flex justifyContent="center" alignItems="center" padding="spacingXl">
|
|
|
<Spinner />
|
|
|
- </div>
|
|
|
+ </Flex>
|
|
|
) : (
|
|
|
- <table className={styles.table}>
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th className={styles.thName}>
|
|
|
- Name <span className={styles.sortIcon}>⇅</span>
|
|
|
- </th>
|
|
|
- <th className={styles.thFields}>Fields</th>
|
|
|
- <th className={styles.thUpdatedBy}>Last Updated By</th>
|
|
|
- <th className={styles.thUpdated}>Updated</th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
+ <Table>
|
|
|
+ <Table.Head>
|
|
|
+ <Table.Row>
|
|
|
+ <Table.Cell className={styles.thName}>
|
|
|
+ <Flex alignItems="center" gap="spacingXs">
|
|
|
+ <Text>Name</Text>
|
|
|
+ <span className={styles.sortIcon}>⇅</span>
|
|
|
+ </Flex>
|
|
|
+ </Table.Cell>
|
|
|
+ <Table.Cell>Fields</Table.Cell>
|
|
|
+ <Table.Cell>Last Updated By</Table.Cell>
|
|
|
+ <Table.Cell>Updated</Table.Cell>
|
|
|
+ </Table.Row>
|
|
|
+ </Table.Head>
|
|
|
+ <Table.Body>
|
|
|
{models.length > 0 ? (
|
|
|
models.map((model) => (
|
|
|
- <tr key={model.id} className={styles.tableRow}>
|
|
|
- <td className={styles.tdName}>
|
|
|
- <div className={styles.modelName}>{model.name}</div>
|
|
|
- <div className={styles.modelId}>{model.id}</div>
|
|
|
- </td>
|
|
|
- <td className={styles.tdFields}>
|
|
|
- {model.fields?.length || 0}
|
|
|
- </td>
|
|
|
- <td className={styles.tdUpdatedBy}>
|
|
|
- <div className={styles.userInfo}>
|
|
|
- <span className={styles.avatar}>👤</span>
|
|
|
- <span>管理员</span>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- <td className={styles.tdUpdated}>
|
|
|
- {new Date(model.updatedAt).toLocaleString("zh-CN", {
|
|
|
- year: "numeric",
|
|
|
- month: "2-digit",
|
|
|
- day: "2-digit",
|
|
|
- hour: "2-digit",
|
|
|
- minute: "2-digit",
|
|
|
- })}
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
+ <Table.Row key={model.id} className={styles.tableRow}>
|
|
|
+ <Table.Cell>
|
|
|
+ <Box>
|
|
|
+ <Text fontWeight="fontWeightMedium">{model.name}</Text>
|
|
|
+ <Text fontSize="fontSizeS" fontColor="gray500">
|
|
|
+ {model.id}
|
|
|
+ </Text>
|
|
|
+ </Box>
|
|
|
+ </Table.Cell>
|
|
|
+ <Table.Cell>
|
|
|
+ <Text>{model.fields?.length || 0}</Text>
|
|
|
+ </Table.Cell>
|
|
|
+ <Table.Cell>
|
|
|
+ <Flex alignItems="center" gap="spacingXs">
|
|
|
+ <Box className={styles.avatar}>👤</Box>
|
|
|
+ <Text>管理员</Text>
|
|
|
+ </Flex>
|
|
|
+ </Table.Cell>
|
|
|
+ <Table.Cell>
|
|
|
+ <Text fontSize="fontSizeS" fontColor="gray600">
|
|
|
+ {new Date(model.updatedAt).toLocaleString("zh-CN", {
|
|
|
+ year: "numeric",
|
|
|
+ month: "2-digit",
|
|
|
+ day: "2-digit",
|
|
|
+ hour: "2-digit",
|
|
|
+ minute: "2-digit",
|
|
|
+ })}
|
|
|
+ </Text>
|
|
|
+ </Table.Cell>
|
|
|
+ </Table.Row>
|
|
|
))
|
|
|
) : (
|
|
|
- <tr>
|
|
|
- <td colSpan={4} className={styles.emptyState}>
|
|
|
- 暂无数据
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
+ <Table.Row>
|
|
|
+ <Table.Cell colSpan={4}>
|
|
|
+ <Flex justifyContent="center" padding="spacingXl">
|
|
|
+ <Text fontColor="gray500">暂无数据</Text>
|
|
|
+ </Flex>
|
|
|
+ </Table.Cell>
|
|
|
+ </Table.Row>
|
|
|
)}
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
+ </Table.Body>
|
|
|
+ </Table>
|
|
|
)}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </Box>
|
|
|
+ </Box>
|
|
|
);
|
|
|
}
|