From 3e227089faa2b33394063bceaaa9abdbbe5990f9 Mon Sep 17 00:00:00 2001 From: vahidrezvani Date: Sun, 22 Feb 2026 08:50:11 +0330 Subject: [PATCH] creat action and ci cd --- .github/workflows/deploy.yml | 51 +++++++++++++ src/app/transaction/page.tsx | 138 ++++++++++++++++++++++------------- src/services/api/routes.ts | 2 +- src/types/transaction.ts | 26 ++++--- 4 files changed, 155 insertions(+), 62 deletions(-) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..9930c55 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,51 @@ +name: CI/CD Pipeline + +on: + push: + branches: + - main # اگر اسم برنچ اصلیت master هست، این رو بکن master + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Docker Image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + didvan/basa_houshan_panel:latest + didvan/basa_houshan_panel:${{ github.sha }} + + - name: Deploy to Server + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.SERVER_IP_BASA_PANEL }} + username: ${{ secrets.SERVER_USERNAME }} + # اگر از کلید SSH استفاده می‌کنی این خط بمونه: + key: ${{ secrets.SERVER_SSH_KEY }} + # اگر از پسورد استفاده می‌کنی، خط بالا رو پاک کن و خط پایین رو از کامنت دربیار: + # password: ${{ secrets.SERVER_PASSWORD }} + script: | + # !!! بسیار مهم: مسیر دقیق پوشه پروژه روی سرورت رو اینجا جایگزین کن !!! + cd /home/ubuntu/app/panel-houshan + + # لاگین به داکر هاب روی سرور (برای دانلود ایمیج‌های Private) + echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + + # دانلود ایمیج جدید و ری‌استارت فقط همون کانتینر + docker compose pull app + docker compose up -d app + + # پاکسازی ایمیج‌های قدیمی + docker image prune -f \ No newline at end of file diff --git a/src/app/transaction/page.tsx b/src/app/transaction/page.tsx index e37ce15..90496a3 100644 --- a/src/app/transaction/page.tsx +++ b/src/app/transaction/page.tsx @@ -9,91 +9,110 @@ import { Receipt, SearchNormal1 } from "iconsax-react"; import { API_ROUTES, request } from "@/services/api"; import { Container } from "@/components/container"; -import type { Transaction } from "@/types/transaction"; +import type { Transaction, TransactionListResponse } from "@/types/transaction"; const { Option } = Select; export default function TransactionsPage() { const [page, setPage] = useState(1); - const [total, setTotal] = useState(0); // filters const [search, setSearch] = useState(""); const [typeFilter, setTypeFilter] = useState(undefined); + const [statusFilter, setStatusFilter] = useState(undefined); - const fetchData = async () => { + const fetchData = async (): Promise => { const res = await request.get(API_ROUTES.TRANSACTION.ALL, { page, - limit: 10, - q: search, - type: typeFilter, + size: 10, + ...(search ? { q: search } : {}), + ...(typeFilter ? { type: typeFilter } : {}), + ...(statusFilter ? { status: statusFilter } : {}), }); - - setTotal(res.data.total_count || 0); - return res.data.transactions as Transaction[]; + return res.data as TransactionListResponse; }; const { data, isLoading } = useQuery({ - queryKey: ["transactions", page, search, typeFilter], + queryKey: ["transactions", page, search, typeFilter, statusFilter], queryFn: fetchData, }); const columns: ColumnsType = [ + { + title: "#", + dataIndex: "id", + key: "id", + width: 60, + render: (id: number) => {id}, + }, { title: "کاربر", dataIndex: "user", key: "user", - render: (user) => - user ? ( + render: (user: Transaction["user"]) => ( +
- {user.username} + {user.name} - ) : ( - سیستمی / حذف شده - ), + {user.mobile_number} +
+ ), }, { - title: "مقدار", + title: "اعتبار", + dataIndex: "credit", + key: "credit", + render: (credit: number) => ( + {credit.toLocaleString()} + ), + }, + { + title: "مبلغ (ریال)", dataIndex: "amount", key: "amount", render: (amount: number) => ( {amount.toLocaleString()} ), }, + { + title: "شماره پیگیری", + dataIndex: "ref_id", + key: "ref_id", + render: (ref_id: string) => ( + {ref_id || "—"} + ), + }, { title: "نوع", dataIndex: "type", key: "type", render: (type: string) => { - let color: "default" | "green" | "blue" | "gold" = "default"; - let text = type; - - switch (type) { - case "gift": - color = "green"; - text = "هدیه"; - break; - case "credit": - color = "blue"; - text = "اعتبار"; - break; - case "free": - color = "gold"; - text = "رایگان"; - break; - } - - return {text}; + const map: Record = { + basa: { color: "purple", label: "باسا" }, + gift: { color: "green", label: "هدیه" }, + credit: { color: "blue", label: "اعتبار" }, + free: { color: "gold", label: "رایگان" }, + }; + const info = map[type] ?? { color: "default", label: type }; + return {info.label}; }, }, { - title: "توضیحات", - dataIndex: "description", - key: "description", - ellipsis: true, + title: "وضعیت", + dataIndex: "status", + key: "status", + render: (status: string) => { + const map: Record = { + success: { color: "success", label: "موفق" }, + failed: { color: "error", label: "ناموفق" }, + pending: { color: "warning", label: "در انتظار" }, + }; + const info = map[status] ?? { color: "default", label: status }; + return {info.label}; + }, }, { title: "تاریخ", @@ -127,7 +146,7 @@ export default function TransactionsPage() {
} - placeholder="جستجو (نام کاربری، توضیحات...)" + placeholder="جستجو (نام، شماره موبایل، شماره پیگیری...)" className="md:w-1/3" allowClear value={search} @@ -137,22 +156,36 @@ export default function TransactionsPage() { - {(search || typeFilter) && ( + + + {(search || typeFilter || statusFilter) && (