Skip to content
Merged

Dev #12

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
5 changes: 5 additions & 0 deletions .changeset/neat-humans-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"commitflow": patch
---

feat: add photo field to User model and update related services
1 change: 1 addition & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ model User {
email String? @unique
phone String?
name String?
photo String?
role Role? @default(USER)
password String? // kalau pake password
session_token String? @unique
Expand Down
2 changes: 2 additions & 0 deletions backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export class AuthService {
id: true,
name: true,
phone: true,
photo: true,
email: true,
members: true,
},
Expand Down Expand Up @@ -141,6 +142,7 @@ export class AuthService {
id: true,
name: true,
phone: true,
photo: true,
email: true,
members: true,
password: true,
Expand Down
15 changes: 11 additions & 4 deletions backend/src/project-management/project-management.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-base-to-string */
import {
Injectable,
NotFoundException,
Expand Down Expand Up @@ -755,17 +756,18 @@ export class ProjectManagementService {
email: email ?? null,
password: hashed ?? null,
phone: phone ?? null,
photo: photo ?? null,
},
});
}
console.log(user);
// create team member
const tm = await tx.teamMember.create({
data: {
name: name ?? "Unnamed",
name: user.name ?? "Unnamed",
role: role ?? null,
email: email ?? null,
photo: photo ?? null,
email: user.email ?? null,
photo: user.photo ? user.photo : photo ?? null,
phone: phone ?? null,
clientId: clientId ?? null,
userId: user.id,
Expand Down Expand Up @@ -818,6 +820,8 @@ export class ProjectManagementService {
if (typeof payload.name !== "undefined") userData.name = payload.name;
if (typeof payload.phone !== "undefined")
userData.phone = payload.phone ?? null;
if (typeof payload.photo !== "undefined")
userData.photo = payload.photo ?? null;
// password must be hashed
if (typeof payload.password !== "undefined" && payload.password !== null) {
userData.password = hashPassword(payload.password);
Expand All @@ -844,10 +848,13 @@ export class ProjectManagementService {
user = await tx.user.update({
where: { id: user.id },
data: userData,
include: {
members: true,
},
});
}
}

console.log(user);
return { teamMember: updatedTeam, user };
});

Expand Down
39 changes: 26 additions & 13 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,37 @@
import Welcome from "./components/Welcome";
import AiAgent from "./components/AiAgent";
import ChatWindow from "./components/ChatWindow";
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import "react-toastify/dist/ReactToastify.css";
import { ToastContainer } from "react-toastify";
import ProjectManagement from "./components/ProjectManagement";
import AuthCard from "./components/Auth/AuthCard";
import { useAuthStore } from "./utils/store";
import { playSound } from "./utils/playSound";
import { getState, saveState } from "./utils/local";

function App() {
const [isShow, setIsShow] = useState(false);
const [isLogin, setIsLogin] = useState(false);
const setAuth = useAuthStore((s) => s.setAuth); // ambil setter dari store

const initSession = useAuthStore((s) => s.initSession);
const token = useAuthStore((s) => s.token);

// untuk kontrol animasi splash logo
const [showLogo, setShowLogo] = useState(true);

const KEY = "isPlaySound";

const [isPlaySound, setIsPlaySound] = useState<boolean>(() => {
const fromLS = getState(KEY);
return fromLS ?? false; // fallback default false
});

useEffect(() => {
saveState(KEY, isPlaySound);
}, [isPlaySound]);

useEffect(() => {
initSession();
}, [isLogin]);
Expand All @@ -29,21 +41,13 @@ function App() {
new Audio("/sounds/send.mp3").load();
new Audio("/sounds/incoming.mp3").load();
new Audio("/sounds/close.mp3").load();
setTimeout(() => {
playSound("/sounds/send.mp3");
}, 1000);
playSound("/sounds/send.mp3", isPlaySound);
}, []);

const onClose = () => {
setIsShow(false);
};

const playSound = (src: string) => {
const audio = new Audio(src);
audio.volume = 0.2;
audio.play().catch(() => {});
};

const handleAuth = (r: any) => {
// r adalah AuthResult dari backend: { token, userId, teamMemberId?, ... }
if (!r || !r.token) {
Expand Down Expand Up @@ -118,7 +122,10 @@ function App() {
visible: { opacity: 1, y: 0, transition: { delay: 0.2 } },
}}
>
<ProjectManagement />
<ProjectManagement
setIsPlaySound={setIsPlaySound}
isPlaySound={isPlaySound}
/>
</motion.div>
</motion.div>
)}
Expand All @@ -139,7 +146,13 @@ function App() {
</motion.div>

{!isShow && <AiAgent setIsShow={setIsShow} />}
{isShow && <ChatWindow onClose={onClose} />}
{isShow && (
<ChatWindow
onClose={onClose}
setIsPlaySound={setIsPlaySound}
isPlaySound={isPlaySound}
/>
)}
</motion.div>
)}

Expand Down
9 changes: 7 additions & 2 deletions frontend/src/components/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@ import { playSound } from "../utils/playSound";

interface ChatWindowProps {
onClose: () => void;
isPlaySound: boolean;
setIsPlaySound: (value: boolean) => void;
}

export default function ChatWindow({ onClose }: ChatWindowProps) {
export default function ChatWindow({
onClose,
isPlaySound,
setIsPlaySound,
}: ChatWindowProps) {
const [input, setInput] = useState("");
const containerRef = useRef<HTMLDivElement>(null);
const [isThinking, setIsThinking] = useState(false);
Expand All @@ -33,7 +39,6 @@ export default function ChatWindow({ onClose }: ChatWindowProps) {
const [placeholder, setPlaceholder] = useState(getRandomPlaceholder());
const [copied, setCopied] = useState(false);
const [shared, setShared] = useState(false);
const [isPlaySound, setIsPlaySound] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [isMessagesReady, setIsMessagesReady] = useState(false);

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/EditProfileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ export default function EditProfileModal({
<input
type="password"
value={password}
autoComplete="false"
onChange={(e) => setPassword(e.target.value)}
placeholder="New password (leave blank to keep)"
className="px-3 py-2 rounded-lg border bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-sky-300 dark:focus:ring-sky-600"
Expand All @@ -321,6 +322,7 @@ export default function EditProfileModal({
<input
type="password"
value={passwordConfirm}
autoComplete="new-password"
onChange={(e) => setPasswordConfirm(e.target.value)}
placeholder="Confirm new password"
className="px-3 py-2 rounded-lg border bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-sky-300 dark:focus:ring-sky-600"
Expand Down
Loading