Skip to content

Commit

Permalink
feat: optimize admin loading features and sharing avatar
Browse files Browse the repository at this point in the history
  • Loading branch information
zmh-program committed Jan 10, 2024
1 parent c3bca6b commit 8e9a2df
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 89 deletions.
2 changes: 1 addition & 1 deletion app/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"package": {
"productName": "chatnio",
"version": "3.8.3"
"version": "3.8.4"
},
"tauri": {
"allowlist": {
Expand Down
135 changes: 72 additions & 63 deletions app/src/admin/api/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,58 @@ import axios from "axios";
import { getErrorMessage } from "@/utils/base.ts";

export async function getAdminInfo(): Promise<InfoResponse> {
const response = await axios.get("/admin/analytics/info");
if (response.status !== 200) {
try {
const response = await axios.get("/admin/analytics/info");
return response.data as InfoResponse;
} catch (e) {
console.warn(e);
return { subscription_count: 0, billing_today: 0, billing_month: 0 };
}

return response.data as InfoResponse;
}

export async function getModelChart(): Promise<ModelChartResponse> {
const response = await axios.get("/admin/analytics/model");
if (response.status !== 200) {
try {
const response = await axios.get("/admin/analytics/model");
const data = response.data as ModelChartResponse;

return {
date: data.date,
value: data.value || [],
};
} catch (e) {
console.warn(e);
return { date: [], value: [] };
}

const data = response.data as ModelChartResponse;

return {
date: data.date,
value: data.value || [],
};
}

export async function getRequestChart(): Promise<RequestChartResponse> {
const response = await axios.get("/admin/analytics/request");
if (response.status !== 200) {
try {
const response = await axios.get("/admin/analytics/request");
return response.data as RequestChartResponse;
} catch (e) {
console.warn(e);
return { date: [], value: [] };
}

return response.data as RequestChartResponse;
}

export async function getBillingChart(): Promise<BillingChartResponse> {
const response = await axios.get("/admin/analytics/billing");
if (response.status !== 200) {
try {
const response = await axios.get("/admin/analytics/billing");
return response.data as BillingChartResponse;
} catch (e) {
console.warn(e);
return { date: [], value: [] };
}

return response.data as BillingChartResponse;
}

export async function getErrorChart(): Promise<ErrorChartResponse> {
const response = await axios.get("/admin/analytics/error");
if (response.status !== 200) {
try {
const response = await axios.get("/admin/analytics/error");
return response.data as ErrorChartResponse;
} catch (e) {
console.warn(e);
return { date: [], value: [] };
}

return response.data as ErrorChartResponse;
}

export async function getInvitationList(
Expand All @@ -84,81 +89,85 @@ export async function generateInvitation(
quota: number,
number: number,
): Promise<InvitationGenerateResponse> {
const response = await axios.post("/admin/invitation/generate", {
type,
quota,
number,
});
if (response.status !== 200) {
return { status: false, data: [], message: "" };
try {
const response = await axios.post("/admin/invitation/generate", {
type,
quota,
number,
});
return response.data as InvitationGenerateResponse;
} catch (e) {
return { status: false, data: [], message: getErrorMessage(e) };
}

return response.data as InvitationGenerateResponse;
}

export async function getRedeemList(): Promise<RedeemResponse> {
const response = await axios.get("/admin/redeem/list");
if (response.status !== 200) {
try {
const response = await axios.get("/admin/redeem/list");
return response.data as RedeemResponse;
} catch (e) {
console.warn(e);
return [];
}

return response.data as RedeemResponse;
}

export async function generateRedeem(
quota: number,
number: number,
): Promise<InvitationGenerateResponse> {
const response = await axios.post("/admin/redeem/generate", {
quota,
number,
});
if (response.status !== 200) {
return { status: false, data: [], message: "" };
try {
const response = await axios.post("/admin/redeem/generate", {
quota,
number,
});
return response.data as InvitationGenerateResponse;
} catch (e) {
return { status: false, data: [], message: getErrorMessage(e) };
}

return response.data as InvitationGenerateResponse;
}

export async function getUserList(
page: number,
search: string,
): Promise<UserResponse> {
const response = await axios.get(
`/admin/user/list?page=${page}&search=${search}`,
);
if (response.status !== 200) {
try {
const response = await axios.get(
`/admin/user/list?page=${page}&search=${search}`,
);
return response.data as UserResponse;
} catch (e) {
return {
status: false,
message: "",
message: getErrorMessage(e),
data: [],
total: 0,
};
}

return response.data as UserResponse;
}

export async function quotaOperation(
id: number,
quota: number,
): Promise<CommonResponse> {
const response = await axios.post("/admin/user/quota", { id, quota });
if (response.status !== 200) {
return { status: false, message: "" };
try {
const response = await axios.post("/admin/user/quota", { id, quota });
return response.data as CommonResponse;
} catch (e) {
return { status: false, message: getErrorMessage(e) };
}

return response.data as CommonResponse;
}

export async function subscriptionOperation(
id: number,
month: number,
): Promise<CommonResponse> {
const response = await axios.post("/admin/user/subscription", { id, month });
if (response.status !== 200) {
return { status: false, message: "" };
try {
const response = await axios.post("/admin/user/subscription", {
id,
month,
});
return response.data as CommonResponse;
} catch (e) {
return { status: false, message: getErrorMessage(e) };
}

return response.data as CommonResponse;
}
7 changes: 7 additions & 0 deletions app/src/assets/admin/broadcast.less
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@
height: 100%;
min-height: 20vh;
}

.empty {
color: hsl(var(--text-secondary)) !important;
font-size: 14px;
margin: auto;
user-select: none;
}
}
23 changes: 23 additions & 0 deletions app/src/assets/admin/dashboard.less
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,28 @@
background: hsl(var(--background));
box-shadow: 0.5rem 0.5rem 1rem 0 var(--shadow);
user-select: none;

.chart {
.chart-title {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: nowrap;

svg {
position: relative;
top: 1px;
}

& > * {
flex-shrink: 0;
margin-right: 0.25rem;

&:last-child {
margin-right: 0;
}
}
}
}
}
}
8 changes: 6 additions & 2 deletions app/src/assets/pages/sharing.less
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,16 @@
user-select: none;
flex-shrink: 0;

img {
img, .avatar {
width: 2rem;
height: 2rem;
border-radius: 4px;
margin-right: 0.75rem;
margin-right: 0.5rem;
flex-shrink: 0;

p {
font-size: 0.85rem;
}
}

span {
Expand Down
16 changes: 13 additions & 3 deletions app/src/components/admin/ChargeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { Badge } from "@/components/ui/badge.tsx";
import { useToast } from "@/components/ui/use-toast";
import { deleteCharge, listCharge, setCharge } from "@/admin/api/charge.ts";
import { useEffectAsync } from "@/utils/hook.ts";
import { cn } from "@/components/ui/lib/utils.ts";

const initialState: ChargeProps = {
id: -1,
Expand Down Expand Up @@ -376,9 +377,10 @@ type ChargeTableProps = {
data: ChargeProps[];
dispatch: (action: any) => void;
onRefresh: () => void;
loading: boolean;
};

function ChargeTable({ data, dispatch, onRefresh }: ChargeTableProps) {
function ChargeTable({ data, dispatch, onRefresh, loading }: ChargeTableProps) {
const { t } = useTranslation();
const { toast } = useToast();

Expand Down Expand Up @@ -449,7 +451,7 @@ function ChargeTable({ data, dispatch, onRefresh }: ChargeTableProps) {
className={`mr-2`}
onClick={onRefresh}
>
<RotateCw className={`h-4 w-4`} />
<RotateCw className={cn("w-4 h-4", loading && "animate-spin")} />
</Button>
</div>
</div>
Expand All @@ -461,13 +463,16 @@ function ChargeWidget() {
const { toast } = useToast();
const [data, setData] = useState<ChargeProps[]>([]);
const [form, dispatch] = useReducer(reducer, initialState);
const [loading, setLoading] = useState(false);

const usedModels = useMemo((): string[] => {
return data.flatMap((charge) => charge.models);
}, [data]);

async function refresh() {
setLoading(true);
const resp = await listCharge();
setLoading(false);
toastState(toast, t, resp);
setData(resp.data);
}
Expand All @@ -482,7 +487,12 @@ function ChargeWidget() {
dispatch={dispatch}
usedModels={usedModels}
/>
<ChargeTable data={data} onRefresh={refresh} dispatch={dispatch} />
<ChargeTable
data={data}
onRefresh={refresh}
dispatch={dispatch}
loading={loading}
/>
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/admin/InvitationTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ function InvitationTable() {
) : (
<div className={`empty`}>
{loading ? (
<Loader2 className={`w-6 h-6 inline-block mr-1 animate-spin`} />
<Loader2 className={`w-6 h-6 inline-block animate-spin`} />
) : (
<p>{t("admin.empty")}</p>
)}
Expand Down
13 changes: 9 additions & 4 deletions app/src/components/admin/RedeemTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { useTranslation } from "react-i18next";
import { useState } from "react";
import { RedeemResponse } from "@/admin/types.ts";
import { Button } from "@/components/ui/button.tsx";
import { Download, RotateCw } from "lucide-react";
import { Download, Loader2, RotateCw } from "lucide-react";
import { generateRedeem, getRedeemList } from "@/admin/api/chart.ts";
import { Input } from "@/components/ui/input.tsx";
import { useToast } from "@/components/ui/use-toast.ts";
Expand Down Expand Up @@ -128,9 +128,12 @@ function GenerateDialog() {
function RedeemTable() {
const { t } = useTranslation();
const [data, setData] = useState<RedeemResponse>([]);
const [loading, setLoading] = useState<boolean>(false);

const sync = async () => {
setLoading(true);
const resp = await getRedeemList();
setLoading(false);
setData(resp ?? []);
};

Expand Down Expand Up @@ -159,10 +162,12 @@ function RedeemTable() {
</TableBody>
</Table>
</>
) : (
<div className={`empty`}>
<p>{t("admin.empty")}</p>
) : loading ? (
<div className={`flex flex-col my-4 items-center`}>
<Loader2 className={`w-6 h-6 inline-block animate-spin`} />
</div>
) : (
<p className={`empty`}>{t("admin.empty")}</p>
)}
<div className={`redeem-action`}>
<div className={`grow`} />
Expand Down
Loading

0 comments on commit 8e9a2df

Please sign in to comment.