Merge pull request #17 from bipproduction/katalog/profile
Katalog/profile
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"@types/node": "20.4.5",
|
||||
"@types/react": "18.2.17",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint": "8.45.0",
|
||||
"eslint-config-next": "13.4.12",
|
||||
@@ -33,6 +34,7 @@
|
||||
"react-simple-toasts": "^5.10.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"typescript": "5.1.6",
|
||||
"uuid": "^9.0.1",
|
||||
"yaml": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/img/0fb20fe0-af14-432b-87ca-094cecb4cb37.webp
Normal file
BIN
public/img/0fb20fe0-af14-432b-87ca-094cecb4cb37.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
public/img/12040bbf-0319-41a0-a87e-268697f14a26.png
Normal file
BIN
public/img/12040bbf-0319-41a0-a87e-268697f14a26.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
public/img/6d767045-b70f-41ef-9775-85407302443c.png
Normal file
BIN
public/img/6d767045-b70f-41ef-9775-85407302443c.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
public/img/716c7533-d96b-4305-ae4f-52fb56dec512.png
Normal file
BIN
public/img/716c7533-d96b-4305-ae4f-52fb56dec512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
11
src/app/api/profile/foto/[name]/route.ts
Normal file
11
src/app/api/profile/foto/[name]/route.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import fs from 'fs'
|
||||
|
||||
export async function GET(req: NextRequest, { params }: { params: { name: string } }) {
|
||||
const fl = fs.readFileSync(`./public/img/${params.name}`)
|
||||
return new NextResponse(fl, {
|
||||
headers: {
|
||||
"Content-Type": "image/png"
|
||||
}
|
||||
})
|
||||
}
|
||||
10
src/app/dev/katalog/profile/upload/layout.tsx
Normal file
10
src/app/dev/katalog/profile/upload/layout.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { UploadFotoProfileLayout } from "@/app_modules/katalog/profile";
|
||||
import { AppShell } from "@mantine/core";
|
||||
|
||||
export default function Layout({ children }: { children: any }) {
|
||||
return (
|
||||
<>
|
||||
<UploadFotoProfileLayout>{children}</UploadFotoProfileLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
9
src/app/dev/katalog/profile/upload/page.tsx
Normal file
9
src/app/dev/katalog/profile/upload/page.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { UploadFotoProfile } from "@/app_modules/katalog/profile";
|
||||
|
||||
export default async function Page() {
|
||||
return (
|
||||
<>
|
||||
<UploadFotoProfile />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -10,4 +10,5 @@ export const ApiHipmi = {
|
||||
//Profile
|
||||
create_profile: "/api/profile/create",
|
||||
edit_profile: "/api/profile/edit",
|
||||
get_foto: "/api/profile/foto/",
|
||||
};
|
||||
|
||||
@@ -34,6 +34,8 @@ import { getProfile } from "../katalog/profile";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAtom } from "jotai";
|
||||
import { gs_token } from "./state/global_state";
|
||||
import { g_getProfile } from "../katalog/profile/fun/fun_get_profile";
|
||||
import { gs_profile } from "../katalog/profile/state/global_state";
|
||||
|
||||
const listHalaman = [
|
||||
{
|
||||
@@ -81,7 +83,7 @@ const listHalaman = [
|
||||
export default function HomeView() {
|
||||
const router = useRouter();
|
||||
const [token, setToken] = useAtom(gs_token);
|
||||
const [profile, setProfile] = useState<any | null>(null);
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
getUserId();
|
||||
@@ -91,13 +93,10 @@ export default function HomeView() {
|
||||
setToken(data);
|
||||
}
|
||||
|
||||
const [profile, setProfile] = useAtom(gs_profile);
|
||||
useShallowEffect(() => {
|
||||
getUserProfile();
|
||||
g_getProfile(setProfile);
|
||||
}, []);
|
||||
async function getUserProfile() {
|
||||
const data = await getProfile();
|
||||
setProfile(data);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -12,9 +12,9 @@ import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import toast from "react-simple-toasts";
|
||||
import { gs_profile } from "../state/global_state";
|
||||
import { g_getProfile } from "../fun/fun-get-profile";
|
||||
import { g_getProfile } from "../fun/fun_get_profile";
|
||||
|
||||
export default function EditProfile({ data }: { data: any }) {
|
||||
export default function EditProfile() {
|
||||
const router = useRouter();
|
||||
|
||||
//Get data profile
|
||||
@@ -24,6 +24,8 @@ export default function EditProfile({ data }: { data: any }) {
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
|
||||
async function onUpdate() {
|
||||
const body = profile;
|
||||
if (_.values(body).includes("")) return toast("Lengkapi data");
|
||||
@@ -51,8 +53,8 @@ export default function EditProfile({ data }: { data: any }) {
|
||||
<>
|
||||
{/* {JSON.stringify(profile)} */}
|
||||
<Stack px={"sm"}>
|
||||
<TextInput label="Username" disabled value={data.User.username} />
|
||||
<TextInput label="Nomor" disabled value={data.User.nomor} />
|
||||
<TextInput label="Username" disabled value={profile.User.username} />
|
||||
<TextInput label="Nomor" disabled value={profile.User.nomor} />
|
||||
|
||||
<TextInput
|
||||
label="Nama"
|
||||
|
||||
@@ -24,13 +24,7 @@ export async function getProfile() {
|
||||
alamat: true,
|
||||
jenisKelamin: true,
|
||||
active: true,
|
||||
ImageProfile: {
|
||||
select: {
|
||||
id: true,
|
||||
url: true,
|
||||
active: true,
|
||||
},
|
||||
},
|
||||
imagesId: true,
|
||||
User: {
|
||||
select : {
|
||||
username: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { myConsole } from "@/app/fun/my_console";
|
||||
import { getProfile } from "..";
|
||||
|
||||
/**
|
||||
@@ -6,6 +7,9 @@ import { getProfile } from "..";
|
||||
* @returns data profile
|
||||
*/
|
||||
export async function g_getProfile(setProfile: any) {
|
||||
const data = await getProfile().then((res) => res);
|
||||
setProfile(data)
|
||||
}
|
||||
await getProfile()
|
||||
.then((res) => res)
|
||||
.then((val) => {
|
||||
setProfile(val);
|
||||
});
|
||||
}
|
||||
16
src/app_modules/katalog/profile/fun/get_foto_profile.ts
Normal file
16
src/app_modules/katalog/profile/fun/get_foto_profile.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
"use server";
|
||||
import { myConsole } from "@/app/fun/my_console";
|
||||
import prisma from "@/app/lib/prisma";
|
||||
|
||||
export async function getFotoProfile(id: any) {
|
||||
const imgUrl = await prisma.images.findUnique({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
url: true,
|
||||
},
|
||||
});
|
||||
return imgUrl;
|
||||
}
|
||||
52
src/app_modules/katalog/profile/fun/upload_foto.ts
Normal file
52
src/app_modules/katalog/profile/fun/upload_foto.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
"use server";
|
||||
|
||||
import { myConsole } from "@/app/fun/my_console";
|
||||
import prisma from "@/app/lib/prisma";
|
||||
import fs from "fs";
|
||||
import _ from "lodash";
|
||||
import { cookies } from "next/headers";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param formData
|
||||
* @returns upload gambar ke /public/img
|
||||
*/
|
||||
export async function funUploadFoto(formData: FormData, id: string) {
|
||||
const file: any = formData.get("file");
|
||||
const fName = file.name;
|
||||
const fExt = _.lowerCase(file.name.split(".").pop());
|
||||
const fRandomName = v4(fName) + "." + fExt;
|
||||
|
||||
myConsole(id);
|
||||
myConsole(fExt);
|
||||
|
||||
const upload = await prisma.images.create({
|
||||
data: {
|
||||
url: fRandomName,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
url: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (upload) {
|
||||
await prisma.profile.update({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
data: {
|
||||
imagesId: upload.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const upFolder = Buffer.from(await file.arrayBuffer());
|
||||
fs.writeFileSync(`./public/img/${upload.url}`, upFolder);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: upload,
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,17 @@
|
||||
import ProfileLayout from "./create/layout";
|
||||
import CreateProfile from "./create/view";
|
||||
import {getProfile} from "./fun/api-get-profile";
|
||||
import EditProfileLayout from './edit/layout';
|
||||
import EditProfileView from './edit/view'
|
||||
import { getProfile } from "./fun/api-get-profile";
|
||||
import EditProfileLayout from "./edit/layout";
|
||||
import EditProfileView from "./edit/view";
|
||||
import UploadFotoProfile from "./upload/view";
|
||||
import UploadFotoProfileLayout from "./upload/layout";
|
||||
|
||||
|
||||
export {ProfileLayout, CreateProfile, getProfile, EditProfileView, EditProfileLayout}
|
||||
export {
|
||||
ProfileLayout,
|
||||
CreateProfile,
|
||||
getProfile,
|
||||
EditProfileView,
|
||||
EditProfileLayout,
|
||||
UploadFotoProfile,
|
||||
UploadFotoProfileLayout,
|
||||
};
|
||||
|
||||
94
src/app_modules/katalog/profile/upload/layout.tsx
Normal file
94
src/app_modules/katalog/profile/upload/layout.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
ActionIcon,
|
||||
AppShell,
|
||||
FileButton,
|
||||
Flex,
|
||||
Footer,
|
||||
Group,
|
||||
Header,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import { IconArrowLeft, IconUpload } from "@tabler/icons-react";
|
||||
import { useAtom } from "jotai";
|
||||
import toast from "react-simple-toasts";
|
||||
import { gs_profile } from "../state/global_state";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { g_getProfile } from "../fun/fun_get_profile";
|
||||
import { funUploadFoto } from "../fun/upload_foto";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function UploadFotoProfileLayout({
|
||||
children,
|
||||
}: {
|
||||
children: any;
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const [profile, setProfile] = useAtom(gs_profile);
|
||||
useShallowEffect(() => {
|
||||
g_getProfile(setProfile);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppShell
|
||||
header={
|
||||
<Header height={50} px={"sm"}>
|
||||
<Group position="apart" h={50}>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
onClick={() => router.push("/dev/katalog/view")}
|
||||
>
|
||||
<IconArrowLeft />
|
||||
</ActionIcon>
|
||||
<Text>Upload Foto Profile</Text>
|
||||
<ActionIcon variant="transparent"></ActionIcon>
|
||||
</Group>
|
||||
</Header>
|
||||
}
|
||||
footer={
|
||||
<Footer height={70}>
|
||||
<Flex align={"center"} justify={"center"} h={70} gap={"xl"}>
|
||||
<Flex align={"center"} justify={"center"} h={70}>
|
||||
<Flex direction={"column"} align={"center"}>
|
||||
<FileButton
|
||||
onChange={async (files) => {
|
||||
const id = profile?.id
|
||||
|
||||
if (!files) return toast("File Kosong");
|
||||
const fd = new FormData();
|
||||
fd.append("file", files);
|
||||
|
||||
const upFoto = await funUploadFoto(fd, id);
|
||||
if (upFoto.success) {
|
||||
toast("Upload berhasil");
|
||||
router.push("/dev/katalog/view")
|
||||
// loadDataProfile(valUser.id, setUser, setProfile);
|
||||
}
|
||||
}}
|
||||
accept="image/png,image/jpeg,image/webp"
|
||||
>
|
||||
{(props) => (
|
||||
<ActionIcon {...props}>
|
||||
<IconUpload />
|
||||
</ActionIcon>
|
||||
)}
|
||||
</FileButton>
|
||||
<Text fz={"sm"} fw={"bold"}>
|
||||
Upload
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{/* */}
|
||||
</Footer>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
{/* {JSON.stringify(profile)} */}
|
||||
</AppShell>
|
||||
</>
|
||||
);
|
||||
}
|
||||
39
src/app_modules/katalog/profile/upload/view.tsx
Normal file
39
src/app_modules/katalog/profile/upload/view.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import { AspectRatio, FileButton, Image, Paper, Title } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { useAtom } from "jotai";
|
||||
import { g_getProfile } from "../fun/fun_get_profile";
|
||||
import { gs_profile } from "../state/global_state";
|
||||
import { getFotoProfile } from "../fun/get_foto_profile";
|
||||
import { useState } from "react";
|
||||
import { ApiHipmi } from "@/app/lib/api";
|
||||
import { myConsole } from "@/app/fun/my_console";
|
||||
|
||||
export default function UploadFotoProfile() {
|
||||
const [profile, setProfile] = useAtom(gs_profile);
|
||||
useShallowEffect(() => {
|
||||
g_getProfile(setProfile);
|
||||
}, []);
|
||||
|
||||
const [foto, setFoto] = useState<any | null>(null);
|
||||
useShallowEffect(() => {
|
||||
if (profile?.imagesId === undefined || profile?.imagesId === null) {
|
||||
myConsole("Waiting data");
|
||||
} else {
|
||||
getFotoProfile(profile?.imagesId).then((res) => setFoto(res?.url));
|
||||
}
|
||||
}, [profile?.imagesId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* {JSON.stringify(foto)} */}
|
||||
<AspectRatio ratio={1 / 1} >
|
||||
<Paper p={"lg"}>
|
||||
{foto ? <Image alt="" src={ApiHipmi.get_foto + `${foto}`} /> : <Image alt="" src={"/aset/avatar.png"} />}
|
||||
</Paper>
|
||||
</AspectRatio>
|
||||
{/* {JSON.stringify(profile)} */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -29,7 +29,9 @@ import { getProfile } from "../profile";
|
||||
import { gs_profile } from "../profile/state/global_state";
|
||||
import { myConsole } from "@/app/fun/my_console";
|
||||
import { useAtom } from "jotai";
|
||||
import { g_getProfile } from "../profile/fun/fun-get-profile";
|
||||
import { g_getProfile } from "../profile/fun/fun_get_profile";
|
||||
import { getFotoProfile } from "../profile/fun/get_foto_profile";
|
||||
import { ApiHipmi } from "@/app/lib/api";
|
||||
|
||||
export default function KatalogView() {
|
||||
const router = useRouter();
|
||||
@@ -40,20 +42,51 @@ export default function KatalogView() {
|
||||
g_getProfile(setProfile);
|
||||
}, []);
|
||||
|
||||
const [foto, setFoto] = useState<any | null>(null);
|
||||
useShallowEffect(() => {
|
||||
if (profile?.imagesId === undefined || profile?.imagesId === null) {
|
||||
myConsole("Waiting data");
|
||||
} else {
|
||||
getFotoProfile(profile?.imagesId).then((res) => setFoto(res?.url));
|
||||
}
|
||||
myConsole(profile?.imagesId);
|
||||
}, [profile?.imagesId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Background dan foto */}
|
||||
<Box>
|
||||
<Paper bg={"gray"} p={"md"}>
|
||||
<Image alt="" src={"/aset/logo.png"} />
|
||||
</Paper>
|
||||
<Center>
|
||||
<Image
|
||||
alt=""
|
||||
src={"/aset/avatar.png"}
|
||||
height={100}
|
||||
width={100}
|
||||
sx={{ position: "absolute", marginBottom: 10, paddingBottom: 10 }}
|
||||
/>
|
||||
{foto ? (
|
||||
<Image
|
||||
radius={50}
|
||||
alt=""
|
||||
src={ApiHipmi.get_foto + `${foto}`}
|
||||
height={100}
|
||||
width={100}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
marginBottom: 10,
|
||||
paddingBottom: 10,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
radius={50}
|
||||
alt=""
|
||||
src={"/aset/avatar.png"}
|
||||
height={100}
|
||||
width={100}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
marginBottom: 10,
|
||||
paddingBottom: 10,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Center>
|
||||
<Center>
|
||||
<ActionIcon
|
||||
@@ -62,7 +95,7 @@ export default function KatalogView() {
|
||||
variant="transparent"
|
||||
bg={"gray"}
|
||||
radius={50}
|
||||
// onClick={() => router.push("/dev/katalog/profile/upload")}
|
||||
onClick={() => router.push("/dev/katalog/profile/upload")}
|
||||
sx={{ position: "relative" }}
|
||||
>
|
||||
<IconCamera color="black" size={20} />
|
||||
@@ -70,6 +103,7 @@ export default function KatalogView() {
|
||||
</Center>
|
||||
</Box>
|
||||
|
||||
{/* Username dan Nama */}
|
||||
<Group position="apart">
|
||||
<Flex direction={"column"} mt={"lg"}>
|
||||
<Text fz={"lg"} fw={"bold"}>
|
||||
@@ -87,6 +121,7 @@ export default function KatalogView() {
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
|
||||
{/* Info user: nomor, email dll */}
|
||||
<Flex direction={"column"} pt={"lg"}>
|
||||
<Grid>
|
||||
<Grid.Col span={"content"}>
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -756,6 +756,11 @@
|
||||
"@types/mime" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/uuid@^9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.4.tgz#e884a59338da907bda8d2ed03e01c5c49d036f1c"
|
||||
integrity sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==
|
||||
|
||||
"@typescript-eslint/parser@^5.42.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
|
||||
@@ -3533,6 +3538,11 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
uuid@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||
|
||||
watchpack@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||
|
||||
Reference in New Issue
Block a user