Skip to content

Commit

Permalink
Merge pull request #54 from Sports-day/feature/#25-image-upload-feature
Browse files Browse the repository at this point in the history
画像アップロード機能 Feature/#25 image upload feature
  • Loading branch information
1nayu authored May 22, 2024
2 parents 84b915c + 5e824a7 commit 758d797
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 227 deletions.
41 changes: 41 additions & 0 deletions app/(authenticated)/images/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {Alert, Breadcrumbs, Button, Link, Stack, Typography} from "@mui/material";
import CardBackground from "@/components/layout/cardBackground";
import {imageFactory} from "@/src/models/ImageModel";
import ImageEditor from "@/components/images/imageEditor";

export default async function ImageEditPage({params}: { params: { id: string } }) {
const imageId = parseInt(params.id, 10)
const image = await imageFactory().show(imageId)

if (isNaN(imageId) || !image) {
return (
<Stack spacing={1} mx={2} my={3}>
<Alert severity="error">
<Typography>画像が削除されました。</Typography>
</Alert>

<Button variant="contained" href="/images/">
画像管理に戻る
</Button>
</Stack>
)
}

return (
<Stack spacing={1} mx={2} my={3}>
<Breadcrumbs aria-label="breadcrumb" sx={{pl: 2}}>
<Link underline="hover" color="inherit" href="/">
管理者のダッシュボード
</Link>
<Link underline="hover" color="inherit" href={"/images/"}>
画像管理
</Link>
<Typography color="text.primary">{image.id}</Typography>
</Breadcrumbs>

<CardBackground title={`画像の情報`}>
<ImageEditor image={image}/>
</CardBackground>
</Stack>
)
}
25 changes: 25 additions & 0 deletions app/(authenticated)/images/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {Stack, Breadcrumbs, Link, Typography} from "@mui/material";
import CardBackground from "@/components/layout/cardBackground";
import ImageCreator from "@/components/images/imageCreator";

export default function ImageCreatePage() {

return (
<Stack spacing={1} mx={2} my={3}>
<Breadcrumbs aria-label="breadcrumb" sx={{pl: 2}}>
<Link underline="hover" color="inherit" href="/">
管理者のダッシュボード
</Link>
<Link underline="hover" color="inherit" href={"/images/"}>
画像管理
</Link>
<Typography color="text.primary">画像作成</Typography>
</Breadcrumbs>
<CardBackground
title={"画像作成"}
>
<ImageCreator/>
</CardBackground>
</Stack>
);
}
37 changes: 37 additions & 0 deletions app/(authenticated)/images/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {Stack, Breadcrumbs, Link, Typography, Avatar, Button} from "@mui/material";
import CardBackground from "@/components/layout/cardBackground";
import {imageFactory} from "@/src/models/ImageModel";
import React from "react";

export default async function ImagesPage() {
const images = await imageFactory().index()

return (
<Stack spacing={1} mx={2} my={3}>
<Breadcrumbs aria-label="breadcrumb" sx={{pl: 2}}>
<Link underline="hover" color="inherit" href="/">
管理者のダッシュボード
</Link>
<Typography color="text.primary">画像管理</Typography>
</Breadcrumbs>
<CardBackground
title={"すべてのタグ"}
button={"作成"}
link={"/images/create"}
>
<Stack spacing={1}>
{images?.map((image) => (
<Button variant={"contained"} fullWidth href={`/images/${image.id}`} key={image.id}>
<Stack direction={"row"} spacing={2} alignItems={"center"} sx={{width:"100%"}}>
<Avatar src={`${process.env.NEXT_PUBLIC_API_URL}/images/${image.id}/file`}/>
<Typography>
{image.id}
</Typography>
</Stack>
</Button>
))}
</Stack>
</CardBackground>
</Stack>
);
}
95 changes: 95 additions & 0 deletions components/images/imageCreator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
'use client'
import {Avatar, Stack} from "@mui/material";
import {useRouter} from "next/navigation";
import React, {useState} from "react";
import {imageFactory} from "@/src/models/ImageModel";

export default function ImageCreator() {
const router = useRouter()
// State to store the file
const [file, setFile] = useState<File | null>(null);

// State to store the base64
const [base64, setBase64] = useState<string | null>(null);

// When the file is selected, set the file state
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) {
return;
}

setFile(e.target.files[0]);
};

// On click, clear the input value
const onClick = (e: React.MouseEvent<HTMLInputElement>) => {
e.currentTarget.value = "";
};

const toBase64 = (file: File) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();

fileReader.readAsDataURL(file);

fileReader.onload = () => {
if (typeof fileReader.result === 'string') {
// Split the data URL at the comma and take the second part (the actual Base64 data)
const base64Data = fileReader.result.split(",")[1];
resolve(base64Data);
} else {
reject('FileReader result is not a string');
}
};

fileReader.onerror = (error) => {
reject(error);
};
});
};

// On submit, upload the file
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (!file) {
return;
}

// Convert the file to base64
const base64 = await toBase64(file as File);

setBase64(base64 as string);

// You can upload the base64 to your server here
await imageFactory().create({
data: base64 as string,
// attachment: imageBase64
})
router.push('/images')

// Clear the states after upload
setFile(null);
setBase64(null);
};

return (
<Stack
spacing={1}
>
<form method="POST" encType="multipart/form-data" onSubmit={handleSubmit}>
<input
type="file"
name="avatar"
accept="image/*"
onChange={onFileChange}
onClick={onClick}
/>
<button type="submit">Upload</button>
</form>
{base64 && (
<Avatar src={base64}/>
)}
</Stack>
)
}
35 changes: 35 additions & 0 deletions components/images/imageEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client'
import {Avatar, Button, Stack, Typography} from "@mui/material";
import {useRouter} from "next/navigation";
import React from "react";
import {imageFactory, Image} from "@/src/models/ImageModel";

export type ImageEditorProps = {
image: Image
}

export default function ImageEditor(props: ImageEditorProps) {
const router = useRouter()

const handleSubmit = async () => {
await imageFactory().delete(props.image.id);
router.back()
router.refresh()
};

return (
<Stack
spacing={1}
>
<Stack direction={"row"} spacing={2} alignItems={"center"}>
<Avatar src={`${process.env.NEXT_PUBLIC_API_URL}/images/${props.image.id}/file`}/>
<Typography>
画像ID:{props.image.id}
</Typography>
</Stack>
<Button variant={"contained"} onClick={handleSubmit}>
削除
</Button>
</Stack>
)
}
6 changes: 4 additions & 2 deletions components/layout/buttonLarge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ export const ButtonLarge: React.FC<ButtonLargeProps> = ({img, children, link})=>
justifyContent={"flex-start"}
alignItems="center"
>
{img && <Avatar sx={{mr:1.5, height: "1.5em", width: "1.5em"}}>

{img && <Avatar
sx={{mr:1.5, height: "1.5em", width: "1.5em"}}
src={img}
>
</Avatar>}
{children}
</Stack>
Expand Down
16 changes: 15 additions & 1 deletion components/layout/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
HiUser,
HiUserGroup,
HiTrophy,
HiMapPin, HiIdentification, HiMiniTag
HiMapPin, HiIdentification, HiMiniTag, HiPhoto
} from "react-icons/hi2";
import {SiGithub} from "react-icons/si";
import WiderLogo from "@/components/svg/wider";
Expand Down Expand Up @@ -191,6 +191,20 @@ export const Navigation = () => {
<Typography>タグ</Typography>
</Stack>
</Button>
<Button
variant={"contained"}
sx={{py: buttonPadding, width: "100%", fontWeight: "600"}}
component={Link}
href={"/images"}
>
<Stack spacing={1} mx={1} width={"100%"} direction={"row"} justifyContent={"flex-start"}
alignItems="center">
<SvgIcon fontSize={"small"}>
<HiPhoto/>
</SvgIcon>
<Typography>画像</Typography>
</Stack>
</Button>
</Stack>

<Stack spacing={1} width={"100%"} direction={"row"} justifyContent={"space-around"}
Expand Down
1 change: 0 additions & 1 deletion components/sports/leagueList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default function LeagueList(props: LeagueListProps) {
{props.games.map((game) => (
<ButtonLarge
key={game.id}
img={"a"}
link={`/sports/${sportId}/games/${game.id}`}
>
{game.name}
Expand Down
2 changes: 1 addition & 1 deletion components/sports/sportEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function SportEditor(props: SportEditorProps) {
>
{images.map((image) => (
<MenuItem key={image.id} value={image.id}>
<Avatar src={image.data}/>
<Avatar src={`${process.env.NEXT_PUBLIC_API_URL}/images/${image.id}/file`}/>
{image.id}
</MenuItem>
))}
Expand Down
2 changes: 1 addition & 1 deletion components/sports/sportsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function SportsList(props: SportsListProps) {
{props.sports.map((sport) => (
<ButtonLarge
key={sport.id}
img={"a"}
img={`${process.env.NEXT_PUBLIC_API_URL}/images/${sport.iconId}/file`}
link={`/sports/${sport.id}`}
>
{sport.name}
Expand Down
Loading

0 comments on commit 758d797

Please sign in to comment.