Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Card component #1

Merged
merged 4 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 79 additions & 13 deletions packages/hardhat/contracts/ExpirableERC721.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
* @title ExpirableERC721
* @dev ERC721 Token that can be minted in batches and supports token expiration.
*/
contract ExpirableERC721 is ERC721, Ownable {
contract ExpirableERC721 is ERC721Enumerable, Ownable {
uint256 private _currentTokenId = 0;
mapping(uint256 => uint256) private _tokenExpirations;

event TokenMinted(address indexed to, uint256 indexed tokenId, uint256 expirationTime);
/**
* @dev Struct to store the schema information for each token.
*/
struct Voucher {
string title;
uint256 issueDate;
uint256 amount;
uint256 expiryDate;
string usageScope;
}

// Mapping from token ID to Voucher details
mapping(uint256 => Voucher) private _tokenVouchers;

/**
* @dev Emitted when a token is minted.
*/
event TokenMinted(
address indexed to,
uint256 indexed tokenId,
string title,
uint256 issueDate,
uint256 amount,
uint256 expiryDate,
string usageScope
);

/**
* @dev Constructor that initializes the ERC721 token with a name, symbol, and sets the initial owner.
Expand All @@ -26,24 +51,65 @@ contract ExpirableERC721 is ERC721, Ownable {
address initialOwner
) ERC721(name, symbol) Ownable(initialOwner) {}

/**
* @dev Mints multiple tokens to a single address with specified expiration times.
* @param to The address to mint the tokens to.
* @param numberOfTokens The number of tokens to mint.
* @param expirationTimes An array of expiration timestamps for each token.
*/
function mintBatch(address to, uint256 numberOfTokens, uint256[] memory expirationTimes) external onlyOwner {
require(numberOfTokens == expirationTimes.length, "Mismatched inputs");
function mintBatch(
address to,
uint256 numberOfTokens,
string memory title,
uint256 issueDate,
uint256 amount,
uint256 expiryDate,
string memory usageScope
) external {
require(to != address(0), "Cannot mint to zero address");
require(numberOfTokens > 0, "Must mint at least one token");

for (uint256 i = 0; i < numberOfTokens; i++) {
uint256 newTokenId = _currentTokenId;
_mint(to, newTokenId);
_tokenExpirations[newTokenId] = expirationTimes[i];
emit TokenMinted(to, newTokenId, expirationTimes[i]);

// Assign the schema data to the newly minted token
_tokenVouchers[newTokenId] = Voucher({
title: title,
issueDate: issueDate,
amount: amount,
expiryDate: expiryDate,
usageScope: usageScope
});

emit TokenMinted(
to,
newTokenId,
title,
issueDate,
amount,
expiryDate,
usageScope
);

_currentTokenId++;
}
}

/**
* @dev Returns a list of Voucher structs owned by a specific address.
* @param owner The address to query vouchers for.
* @return An array containing the Voucher structs owned by the address.
*
* @notice Be cautious when calling this function with a large number of tokens,
* as it may consume a lot of memory and exceed block gas limits.
*/
function listVouchers(address owner) external view returns (Voucher[] memory) {
uint256 balance = balanceOf(owner);
Voucher[] memory vouchers = new Voucher[](balance);

for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = tokenOfOwnerByIndex(owner, i);
vouchers[i] = _tokenVouchers[tokenId];
}
return vouchers;
}


/**
* @dev Returns the expiration time of a specific token.
* @param tokenId The ID of the token.
Expand Down
39 changes: 39 additions & 0 deletions packages/nextjs/app/user/list/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { NextPage } from "next";
import { CardList } from "~~/components/CardList";

const UserListPage: NextPage = () => {
const cards = [
{
title: "引換券",
issueDate: "2024.12.05",
amount: 7000,
expiryDate: "2025.06.30",
usageScope: "店舗全体",
},
{
title: "引換券",
issueDate: "2024.12.05",
amount: 7000,
expiryDate: "2025.06.30",
usageScope: "店舗全体",
},
{
title: "引換券",
issueDate: "2024.12.05",
amount: 7000,
expiryDate: "2025.06.30",
usageScope: "店舗全体",
},
{
title: "引換券",
issueDate: "2024.12.05",
amount: 7000,
expiryDate: "2025.06.30",
usageScope: "店舗全体",
},
];

return <CardList cards={cards} />;
};

export default UserListPage;
65 changes: 65 additions & 0 deletions packages/nextjs/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use client";

import React from "react";
import { Box, CardContent, Card as MuiCard, Typography } from "@mui/material";

type CardProps = {
title: string; // タイトル
issueDate: string; // 発行日
amount: number; // 金額
expiryDate: string; // 有効期限
usageScope: string; // 使用範囲
};

export const Card = ({ title, issueDate, amount, expiryDate, usageScope }: CardProps) => {
return (
<MuiCard
sx={{
width: 300,
borderRadius: 2,
boxShadow: 3,
backgroundColor: "#f9f9f9",
}}
>
<CardContent>
<Typography variant="h4" sx={{ fontWeight: "bold", marginBottom: 3, textAlign: "center" }}>
{title}
</Typography>

<Typography variant="body2" color="text.secondary" sx={{ textAlign: "right", marginBottom: 2 }}>
{issueDate} 発行
</Typography>

<Box
sx={{
fontSize: "24px",
fontWeight: "bold",
textAlign: "center",
color: "#000",
marginBottom: 3,
}}
>
{amount}円
</Box>

<Box
sx={{
display: "flex",
justifyContent: "space-between",
fontSize: "14px",
color: "text.secondary",
marginBottom: 2,
}}
>
<Typography variant="body2" color="text.secondary">
有効期限: {expiryDate}
</Typography>
</Box>

<Typography variant="body2" color="text.secondary">
使用範囲: {usageScope}
</Typography>
</CardContent>
</MuiCard>
);
};
41 changes: 41 additions & 0 deletions packages/nextjs/components/CardList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import { Card } from "~~/components/Card";

type CardData = {
title: string;
issueDate: string;
amount: number;
expiryDate: string;
usageScope: string;
};

type CardListProps = {
cards: CardData[];
};

export const CardList = ({ cards }: CardListProps) => {
return (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
gap: "16px",
justifyContent: "center",
alignItems: "center",
width: "70%",
margin: "100px auto",
}}
>
{cards.map((card, index) => (
<Card
key={index}
title={card.title}
issueDate={card.issueDate}
amount={card.amount}
expiryDate={card.expiryDate}
usageScope={card.usageScope}
/>
))}
</div>
);
};
Loading
Loading