Merge branch 'new-window-v2' of http://git.hicglobalsolutions.in/BhawanSingh/va-tracker - 22 Dec After Merge
This commit is contained in:
commit
4b146ff5e9
|
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
[Interface]
|
[Interface]
|
||||||
|
PrivateKey = gPqf7ScXv2xtLkZz5a8qa7RNJ7uU5TQUo/rTfcprMWE=
|
||||||
|
Address = 10.0.0.172/32
|
||||||
PrivateKey = wHjS7rJYnAGhlvj+XYLefAzki1uRM2xz95IxRML2q24=
|
PrivateKey = wHjS7rJYnAGhlvj+XYLefAzki1uRM2xz95IxRML2q24=
|
||||||
Address = 10.0.0.169/32
|
Address = 10.0.0.169/32
|
||||||
DNS = 1.1.1.1
|
DNS = 1.1.1.1
|
||||||
|
|
|
||||||
|
|
@ -204,11 +204,11 @@ ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate }
|
||||||
|
|
||||||
// 🧠 Append query params only if dates are provided
|
// 🧠 Append query params only if dates are provided
|
||||||
if (fromDate && toDate) {
|
if (fromDate && toDate) {
|
||||||
const params = new URLSearchParams({
|
// const params = new URLSearchParams({
|
||||||
from: fromDate,
|
// from: fromDate,
|
||||||
to: toDate,
|
// to: toDate,
|
||||||
});
|
// });
|
||||||
url += `?${params.toString()}`;
|
url += `/${fromDate}/${toDate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("url", url)
|
console.log("url", url)
|
||||||
|
|
@ -511,15 +511,24 @@ ipcMain.handle("update-payment", async (event, data: Record<string, any>) => {
|
||||||
paymentMethod: data.paymentMethod,
|
paymentMethod: data.paymentMethod,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// paymentMethod: 'payoneeraccount',
|
||||||
|
// payoneerAccount: 'check@gmail.com'
|
||||||
|
|
||||||
// Add payment-specific fields
|
// Add payment-specific fields
|
||||||
if (data.paymentMethod === "wise" && data.wiseEmail) {
|
if (data.paymentMethod === "wise" && data.wiseEmail) {
|
||||||
userDetails.wiseEmail = data.wiseEmail;
|
userDetails.wiseEmail = data.wiseEmail;
|
||||||
// Clear veemAccount when switching to wise
|
// Clear veemAccount when switching to wise
|
||||||
userDetails.veemAccount = "";
|
userDetails.veemAccount = "";
|
||||||
|
userDetails.payoneerAccount = ""
|
||||||
} else if (data.paymentMethod === "veem" && data.veemAccount) {
|
} else if (data.paymentMethod === "veem" && data.veemAccount) {
|
||||||
userDetails.veemAccount = data.veemAccount;
|
userDetails.veemAccount = data.veemAccount;
|
||||||
// Clear wiseEmail when switching to veem
|
// Clear wiseEmail when switching to veem
|
||||||
userDetails.wiseEmail = "";
|
userDetails.wiseEmail = "";
|
||||||
|
userDetails.payoneerAccount = ""
|
||||||
|
} else if (data.paymentMethod === "payoneeraccount" && data.payoneerAccount) {
|
||||||
|
userDetails.payoneerAccount = data.payoneerAccount;
|
||||||
|
userDetails.veemAccount = "";
|
||||||
|
userDetails.wiseEmail = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build final payload
|
// Build final payload
|
||||||
|
|
|
||||||
|
|
@ -592,7 +592,7 @@ const DashboardLayout = () => {
|
||||||
if (!selectedProject?.projectId) return
|
if (!selectedProject?.projectId) return
|
||||||
|
|
||||||
console.log(selectedProject?.projectId, "SelectedProject ID")
|
console.log(selectedProject?.projectId, "SelectedProject ID")
|
||||||
socketRef.current = io("https://portal-sf.docva.com", {
|
socketRef.current = io("http://dreadingly-unfighting-pandora.ngrok-free.dev", {
|
||||||
transports: ["websocket"],
|
transports: ["websocket"],
|
||||||
secure: true,
|
secure: true,
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { profile } = useSelector((state: RootState) => state.profile);
|
const { profile } = useSelector((state: RootState) => state.profile);
|
||||||
|
|
||||||
const [method, setMethod] = useState(profile?.paymentMethod || "");
|
const [method, setMethod] = useState(profile?.paymentMethod || "");
|
||||||
const [wiseEmail, setWiseEmail] = useState(profile?.wiseEmail || "");
|
const [wiseEmail, setWiseEmail] = useState(profile?.wiseEmail || "");
|
||||||
const [veemAccount, setVeemAccount] = useState(profile?.veemAccount || "");
|
const [veemAccount, setVeemAccount] = useState(profile?.veemAccount || "");
|
||||||
|
const [payoneerAccount, setPayoneerAccount] = useState(profile?.payoneerAccount || "");
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
@ -28,6 +29,7 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
|
||||||
setMethod(profile?.paymentMethod || "");
|
setMethod(profile?.paymentMethod || "");
|
||||||
setWiseEmail(profile?.wiseEmail || "");
|
setWiseEmail(profile?.wiseEmail || "");
|
||||||
setVeemAccount(profile?.veemAccount || "");
|
setVeemAccount(profile?.veemAccount || "");
|
||||||
|
setPayoneerAccount(profile?.payoneerAccount || "")
|
||||||
setError("");
|
setError("");
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
@ -46,12 +48,16 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
|
||||||
return setError("Wise email is required.");
|
return setError("Wise email is required.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method === "wise" && !/^\S+@\S+\.\S+$/.test(wiseEmail)) {
|
if (method === "veem" && !veemAccount.trim()) {
|
||||||
return setError("Enter a valid Wise email.");
|
return setError("Veem email is required.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method === "veem" && !veemAccount.trim()) {
|
if (method === "payoneeraccount" && !payoneerAccount.trim()) {
|
||||||
return setError("Veem account number is required.");
|
return setError("Payoneer email is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((method === "wise" || "veem" || "payoneeraccount") && !/^\S+@\S+\.\S+$/.test(wiseEmail)) {
|
||||||
|
return setError("Enter a valid email.");
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -67,6 +73,8 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
|
||||||
updateData.wiseEmail = wiseEmail.trim();
|
updateData.wiseEmail = wiseEmail.trim();
|
||||||
} else if (method === "veem") {
|
} else if (method === "veem") {
|
||||||
updateData.veemAccount = veemAccount.trim();
|
updateData.veemAccount = veemAccount.trim();
|
||||||
|
}else if (method === "payoneeraccount") {
|
||||||
|
updateData.payoneerAccount = payoneerAccount.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Sending to electron:", updateData);
|
console.log("Sending to electron:", updateData);
|
||||||
|
|
@ -134,6 +142,7 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
|
||||||
<option value="">Select method</option>
|
<option value="">Select method</option>
|
||||||
<option value="wise">Wise</option>
|
<option value="wise">Wise</option>
|
||||||
<option value="veem">Veem</option>
|
<option value="veem">Veem</option>
|
||||||
|
<option value="payoneeraccount">Payoneer account</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -169,6 +178,22 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
{method === "payoneeraccount" && (
|
||||||
|
<div className="form-control mb-4">
|
||||||
|
<label className="label">
|
||||||
|
<span className="text-sm font-medium">Payoneer Account Number</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input input-bordered input-sm"
|
||||||
|
placeholder="Enter Payoneer account"
|
||||||
|
value={payoneerAccount}
|
||||||
|
onChange={(e) => setPayoneerAccount(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<button className="btn btn-sm btn-ghost" onClick={onClose}>
|
<button className="btn btn-sm btn-ghost" onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ const Settings: React.FC = () => {
|
||||||
{profile?.veemAccount && (
|
{profile?.veemAccount && (
|
||||||
<div className="p-2 rounded bg-base-200/40">
|
<div className="p-2 rounded bg-base-200/40">
|
||||||
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5">
|
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5">
|
||||||
🏦 Veem Account
|
🏦 Veem Email
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs font-medium truncate" title={profile?.veemAccount || "N/A"}>
|
<p className="text-xs font-medium truncate" title={profile?.veemAccount || "N/A"}>
|
||||||
{profile?.veemAccount || "N/A"}
|
{profile?.veemAccount || "N/A"}
|
||||||
|
|
@ -126,6 +126,19 @@ const Settings: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{profile?.payoneerAccount && (
|
||||||
|
<div className="p-2 rounded bg-base-200/40">
|
||||||
|
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5 ">
|
||||||
|
🏦 Payoneer Account
|
||||||
|
</p>
|
||||||
|
<p className="text-xs font-medium overflow-auto no-scrollbar" title={profile?.payoneerAccount || "N/A"}>
|
||||||
|
{profile?.payoneerAccount || "N/A"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Location - Full Width */}
|
{/* Location - Full Width */}
|
||||||
<div className="p-2 rounded bg-base-200/40 col-span-2 sm:col-span-3">
|
<div className="p-2 rounded bg-base-200/40 col-span-2 sm:col-span-3">
|
||||||
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5">
|
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
interface TabActivity {
|
interface TabActivity {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -21,13 +22,44 @@ interface Props {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
|
|
||||||
logs: ProjectSession[];
|
logs: ProjectSession[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
|
||||||
|
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
setLogs: React.Dispatch<React.SetStateAction<ProjectSession[]>>;
|
||||||
|
|
||||||
|
projectID: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs, loading }) => {
|
|
||||||
|
const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs, loading, setLoading,
|
||||||
|
setLogs,
|
||||||
|
projectID, }) => {
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
||||||
|
const isFirstRender = React.useRef(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// ⛔ skip first render (parent already fetched)
|
||||||
|
if (isFirstRender.current) {
|
||||||
|
isFirstRender.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ fetch only when date changes
|
||||||
|
fetchLogs(selectedDate);
|
||||||
|
}, [selectedDate]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
|
isFirstRender.current = true;
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getTimeDifference = (from: string, to: string) => {
|
const getTimeDifference = (from: string, to: string) => {
|
||||||
const start = DateTime.fromISO(from);
|
const start = DateTime.fromISO(from);
|
||||||
|
|
@ -43,12 +75,81 @@ const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDayRange = (date: Date) => {
|
||||||
|
const d = DateTime.fromJSDate(date);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fromDate: d.toFormat("yyyy-MM-dd"),
|
||||||
|
toDate: d.toFormat("yyyy-MM-dd"),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handlePrevDay = () => {
|
||||||
|
const prev = new Date(selectedDate);
|
||||||
|
prev.setDate(prev.getDate() - 1);
|
||||||
|
|
||||||
|
setSelectedDate(prev); // triggers useEffect
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleNextDay = () => {
|
||||||
|
const next = new Date(selectedDate);
|
||||||
|
next.setDate(next.getDate() + 1);
|
||||||
|
|
||||||
|
setSelectedDate(next); // triggers useEffect
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const fetchLogs = async (date: Date) => {
|
||||||
|
if (!projectID) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
// HERE
|
||||||
|
const { fromDate, toDate } = getDayRange(date);
|
||||||
|
|
||||||
|
const res = await window.electron.getProjectLogs(
|
||||||
|
projectID,
|
||||||
|
fromDate,
|
||||||
|
toDate
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res?.success && Array.isArray(res.data)) {
|
||||||
|
setLogs(res.data[0]?.sessions || []);
|
||||||
|
} else {
|
||||||
|
setLogs([]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to fetch logs", err);
|
||||||
|
setLogs([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<dialog className="modal modal-open">
|
<dialog className="modal modal-open">
|
||||||
<div className="modal-box max-h-[97vh] max-w-4xl bg-base-100 flex flex-col">
|
<div className="modal-box max-h-[97vh] max-w-4xl bg-base-100 flex flex-col">
|
||||||
<h3 className="font-bold text-xs mb-3">
|
<div className="flex justify-between items-center mb-3">
|
||||||
🗂️ Project Logs — <span className="text-primary">{projectName}</span>
|
<div className="font-bold text-xs">
|
||||||
</h3>
|
🗂️ Project Logs — <span className="text-primary">{projectName}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 text-xs">
|
||||||
|
|
||||||
|
<button className="btn btn-xs btn-primary p-1" onClick={handlePrevDay}><ChevronLeft className="" size={12}/></button>
|
||||||
|
|
||||||
|
<span className="font-semibold">
|
||||||
|
{DateTime.fromJSDate(selectedDate).toFormat("dd LLL yyyy")}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button className="btn btn-xs btn-primary p-1" onClick={handleNextDay}><ChevronRight className="" size={12}/></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex justify-center items-center flex-grow">
|
<div className="flex justify-center items-center flex-grow">
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,13 @@ const timesheetData: TimesheetEntry[] = [
|
||||||
{ id: 5, date: "2025-11-01", project: "AI Model Trainer", hours: 8, status: "Approved", remarks: "Normal day" },
|
{ id: 5, date: "2025-11-01", project: "AI Model Trainer", hours: 8, status: "Approved", remarks: "Normal day" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
interface ProjectSession {
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
totalSessionTime: string;
|
||||||
|
tabs: TabActivity[];
|
||||||
|
}
|
||||||
|
|
||||||
interface Project {
|
interface Project {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
|
|
@ -55,8 +62,8 @@ const Timesheets: React.FC = () => {
|
||||||
|
|
||||||
console.log(" Timesheet in component:", windowLogs);
|
console.log(" Timesheet in component:", windowLogs);
|
||||||
const [projectsData, setProjectsData] = useState<Project[]>([]);
|
const [projectsData, setProjectsData] = useState<Project[]>([]);
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState<ProjectSession[]>([]);
|
||||||
// const [selectedProject, setSelectedProject] = useState<string | null>(null);
|
const [selectedProjectLogId, setSelectedProjectLogId] = useState<string | null>(null);
|
||||||
const [selectedProjectName, setSelectedProjectName] = useState<string>("");
|
const [selectedProjectName, setSelectedProjectName] = useState<string>("");
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
@ -122,7 +129,7 @@ const Timesheets: React.FC = () => {
|
||||||
try {
|
try {
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
// setSelectedProject(projectID);
|
setSelectedProjectLogId(projectID);
|
||||||
setSelectedProjectName(projectName);
|
setSelectedProjectName(projectName);
|
||||||
|
|
||||||
console.log("projectID", projectID)
|
console.log("projectID", projectID)
|
||||||
|
|
@ -290,6 +297,9 @@ const Timesheets: React.FC = () => {
|
||||||
projectName={selectedProjectName}
|
projectName={selectedProjectName}
|
||||||
logs={logs}
|
logs={logs}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
setLoading={setLoading}
|
||||||
|
setLogs={setLogs}
|
||||||
|
projectID={selectedProjectLogId}
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Shield, ShieldAlert, RefreshCw, Wifi, Power } from "lucide-react";
|
import { Shield, ShieldAlert, RefreshCw, Wifi, Power, X } from "lucide-react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|
@ -8,7 +8,7 @@ interface Props {
|
||||||
const VpnGuard: React.FC<Props> = ({ children }) => {
|
const VpnGuard: React.FC<Props> = ({ children }) => {
|
||||||
|
|
||||||
const VPN_API = "http://10.0.0.1";
|
const VPN_API = "http://10.0.0.1";
|
||||||
const DEFAULT_API = "https://portal-sf.docva.com"
|
const DEFAULT_API = "http://dreadingly-unfighting-pandora.ngrok-free.dev"
|
||||||
|
|
||||||
const [checking, setChecking] = useState(true);
|
const [checking, setChecking] = useState(true);
|
||||||
const [vpnConnected, setVpnConnected] = useState(true);
|
const [vpnConnected, setVpnConnected] = useState(true);
|
||||||
|
|
@ -21,6 +21,7 @@ const VpnGuard: React.FC<Props> = ({ children }) => {
|
||||||
const res = await window.electron?.checkRegion();
|
const res = await window.electron?.checkRegion();
|
||||||
console.log("VpnGuard", res);
|
console.log("VpnGuard", res);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
|
const baseRes = await window.electron.updateBaseURL(DEFAULT_API);
|
||||||
setVpnConnected(true);
|
setVpnConnected(true);
|
||||||
} else {
|
} else {
|
||||||
setVpnConnected(false);
|
setVpnConnected(false);
|
||||||
|
|
@ -130,6 +131,7 @@ const VpnGuard: React.FC<Props> = ({ children }) => {
|
||||||
<div className="min-h-screen flex flex-col justify-center items-center bg-base-200 p-4">
|
<div className="min-h-screen flex flex-col justify-center items-center bg-base-200 p-4">
|
||||||
<div className="max-w-md w-full">
|
<div className="max-w-md w-full">
|
||||||
<div className="card bg-base-100 shadow-2xl">
|
<div className="card bg-base-100 shadow-2xl">
|
||||||
|
<X className="mt-2 mr-2 ml-auto text-slate-300 hover:text-slate-400" onClick={() => setVpnConnected(true)} />
|
||||||
<div className="card-body items-center text-center">
|
<div className="card-body items-center text-center">
|
||||||
|
|
||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue