Skip to content

Commit

Permalink
Merge pull request #1 from aki-0517/feat/user-list
Browse files Browse the repository at this point in the history
add Card component
  • Loading branch information
aki-0517 authored Dec 5, 2024
2 parents 1769791 + 487e711 commit 155f41c
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 30 deletions.
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

0 comments on commit 155f41c

Please sign in to comment.