Skip to content
Merged

Sami #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
101 changes: 101 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Contributing to Hirfa (حِرفة)

Thank you for your interest in contributing to **Hirfa**! We welcome contributions from developers of all skill levels. Whether you are fixing a bug, implementing a new feature, or improving documentation, your help is appreciated.

This document provides a set of guidelines and instructions for contributing to the repository.

---

## 🛠️ Tech Stack Overview
Before you start, please familiarize yourself with the tools we use:
- **Framework:** Next.js 16 (App Router)
- **Language:** TypeScript
- **Styling:** Tailwind CSS v4
- **Database / Auth:** Supabase (PostgreSQL)
- **Mobile Wrapper:** Capacitor
- **Package Manager:** `pnpm` (highly recommended over `npm` or `yarn`)

---

## 🚀 How to Contribute

### 1. Setup Your Local Environment
1. Fork the repository and clone your fork:
```bash
git clone https://github.com/your-username/Hirfa.git
cd Hirfa
```
2. Install dependencies using `pnpm`:
```bash
pnpm install
```
3. Set up the `.env.local` file. Copy `.env.example` (if available) or refer to the `README.md` to configure your Supabase keys and SMTP details.
4. Run the development server:
```bash
pnpm run dev
```

### 2. Find an Issue or Propose a Change
- **Issues:** Look for open issues in the GitHub repository. Issues labeled `good first issue` or `help wanted` are great places to start.
- **Proposals:** If you have an idea for a new feature or architectural change, please open an issue first to discuss it with the maintainers.

### 3. Make Your Changes
1. Create a new branch for your feature or bugfix:
```bash
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bugfix-name
```
2. Make your code changes.
3. Test your changes locally. If your change affects the mobile app UI or behavior, consider running the Capacitor build to verify:
```bash
pnpm run build:capacitor
npx cap sync android
```

### 4. Code Standards & Best Practices
- **TypeScript:** Use strict typing. Avoid `any` where possible.
- **Components:** Create reusable components inside the `components/` directory. Keep components small, focused, and purely functional if possible.
- **Next.js App Router:** Ensure you understand the difference between Server Components and Client Components (`"use client"`). Be very careful with `generateStaticParams()` as the app heavily relies on Static Exports (`output: 'export'`) for Capacitor builds.
- **Linting:** Run `pnpm run lint` before committing to ensure code style compliance.
- **Arabic First:** Hirfa is built for the Arabic-speaking world. Ensure UI text is properly translated, respects RTL layouts, and uses the correct terminology.

### 5. Commit Your Changes
We prefer conventional commit messages to keep the history clean and readable:
- `feat:` A new feature
- `fix:` A bug fix
- `docs:` Documentation only changes
- `refactor:` A code change that neither fixes a bug nor adds a feature
- `style:` Changes that do not affect the meaning of the code (white-space, formatting)

Example:
```bash
git commit -m "feat: add user profile picture uploader"
```

### 6. Submit a Pull Request (PR)
1. Push your branch to your fork:
```bash
git push origin feature/your-feature-name
```
2. Open a Pull Request against the `main` branch of the original repository.
3. In your PR description, clearly describe the problem you solved or the feature you added. Link to any relevant issues (e.g., "Closes #12").
4. The GitHub Actions CI pipeline will automatically run to verify that the project builds the Android APK correctly. Make sure all checks pass!

---

## 📱 Working with the Mobile App (Capacitor)
Since Hirfa is compiled into an APK using Capacitor, changes to routing or static generation can break the mobile build.

If you add a new dynamic route (e.g., `app/[id]/page.tsx`), you **must** ensure it works with Next.js static exports.
- If it's a Server Component, export `generateStaticParams()`.
- If it's a Client Component, extract the client logic into a separate file (e.g., `ClientPage.tsx`), and let the `page.tsx` be a Server Component that exports `generateStaticParams()` and returns `<ClientPage />`.

*(If you are unsure, open a PR anyway and a maintainer will help you out!)*

---

## 💬 Community & Support
If you get stuck or have questions, feel free to open a Discussion on GitHub or reach out to the core team.

Thank you for making Hirfa better! ❤️
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,37 @@ pnpm run dev

3. Open [http://localhost:3000](http://localhost:3000) in your browser.

### 🏗️ Build Commands

The project uses Next.js with specialized build commands for different targets:

- `pnpm run build:web` — Standard Next.js production build for web hosting (Vercel, etc.).
- `pnpm run build:capacitor` — Specialized static export build (`output: 'export'`) that compiles the app for Capacitor, generating static HTML/JS files in the `out` directory.

---

## 📱 Mobile App (Capacitor)
## 📱 Mobile App (Capacitor) & CI/CD

Hirfa is built as a mobile-first application and is compiled into a native Android APK using Capacitor.

### GitHub Actions (Automated CI/CD)

Hirfa is built as a mobile-first application and can be compiled into a native Android app using Capacitor.
The project is equipped with a robust GitHub Actions workflow (`.github/workflows/build-apk.yml`) that automatically builds the Android APK upon pushing to any branch.
1. It builds the Next.js app using `pnpm run build:capacitor`.
2. It safely configures Capacitor and runs `npx cap sync android`.
3. It compiles the APK using Gradle (`assembleDebug`).
4. The resulting `app-debug.apk` is available as a downloadable artifact directly from the Actions tab on GitHub.

### Build the Android App
### Manual Local Build

1. Build the Next.js web application:
If you want to build the APK locally on your machine:

1. Build the web assets specifically for Capacitor:
```bash
pnpm run build
pnpm run build:capacitor
```

2. Sync the web assets with the Capacitor Android project:
2. Sync the web assets with the Android project:
```bash
npx cap sync android
```
Expand All @@ -178,7 +195,12 @@ npx cap sync android
```bash
npx cap open android
```
Alternatively, you can use the provided `./build_apk.sh` script to automate the APK generation process on Linux environments.

---

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for detailed instructions on how to set up your environment, follow our coding standards, and submit pull requests.

---

Expand Down
Binary file removed app.jpg
Binary file not shown.
24 changes: 17 additions & 7 deletions app/(main)/worker/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const Empty = ({ I, t }: any) => <div className="bg-[#0A0D1A] rounded-2xl p-8 fl
const Title = ({ t, c }: any) => <div className="flex items-center gap-2"><h2 className="text-sm font-bold">{t}</h2><div className={`w-1 h-4 bg-[${c}] rounded-full`} /></div>

export default function CraftsmanHome() {
const { profile: p, newRequests: reqs, appointments: apps, isAvailable: isAv, activeEmergency: actEm, acceptEmergency: accEm, toggleAvailability: tAv, handleRequest: hReq, handleLogout: hLog } = useHome()
const { profile: p, newRequests: reqs, appointments: apps, isAvailable: isAv, activeEmergency: actEm, acceptEmergency: accEm, toggleAvailability: tAv, handleRequest: hReq, handleLogout: hLog, workerSub } = useHome()
const stats = [{ l: 'طلبات اليوم', v: p?.completed_orders || 0, i: ClipboardCheck, c: '#FF8A00' }, { l: 'الأرباح', v: p?.total_earnings || 0, i: Banknote, c: '#FFB800' }, { l: 'التقييم', v: p?.rating || 0, i: Star, c: '#FFB800' }]
const links = [{ l: 'الجدول', h: '/worker/schedule', i: Calendar }, { l: 'الرسائل', h: '/worker/messages', i: MessageSquare }, { l: 'المحفظة', h: '/worker/wallet', i: Wallet }, { l: 'المعرض', h: '/worker/profile/gallery', i: LayoutGrid }]

Expand All @@ -29,12 +29,22 @@ export default function CraftsmanHome() {
<h4 className="text-sm font-bold text-white mb-1">المطلوب: {actEm.description}</h4>
<p className="text-xs text-[#6B7A99]">العميل: {actEm.client?.full_name || actEm.client?.email || 'عميل'}</p>
</div>
<button
onClick={() => accEm(actEm.id)}
className="w-full bg-[#ED4C5C] hover:bg-[#ED4C5C]/90 text-white font-bold py-3.5 rounded-xl text-xs transition-colors shadow-lg shadow-[#ED4C5C]/20"
>
قبول البلاغ والتحرك فوراً
</button>
{(workerSub === 'master' || workerSub === 'premium') ? (
<button
onClick={() => accEm(actEm.id)}
className="w-full bg-[#ED4C5C] hover:bg-[#ED4C5C]/90 text-white font-bold py-3.5 rounded-xl text-xs transition-colors shadow-lg shadow-[#ED4C5C]/20"
>
قبول البلاغ والتحرك فوراً
</button>
) : (
<Link
href="/worker/subscriptions"
className="w-full bg-[#0A0D1A] border border-[#ED4C5C]/30 text-[#ED4C5C] text-center font-bold py-3.5 rounded-xl text-[11px] transition-colors flex items-center justify-center gap-2"
>
<Star size={14} />
للأسف، استقبال الطوارئ متاح لباقات ماستر وبريميوم فقط. رقي باقتك الآن!
</Link>
)}
</div>
)}

Expand Down
31 changes: 29 additions & 2 deletions app/(main)/worker/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import React from 'react'
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import {
LogOut, Star, Pencil, Fingerprint, LayoutGrid, Calendar, Wallet, CreditCard,
Expand All @@ -14,11 +14,22 @@ import { StatCard } from '@/components/ui/profile/StatCard'
import { MenuGroup } from '@/components/ui/profile/MenuGroup'
import { MenuLink } from '@/components/ui/profile/MenuLink'
import { ProfileAvatarInfo } from '@/components/ui/profile/ProfileAvatarInfo'
import { getActiveWorkerSubscription, WorkerSubscription } from '@/lib/supabase/worker-subscriptions'

export default function ProfilePage() {
const { profile } = useAuth()
const supabase = createClient()
const router = useRouter()
const [subscription, setSubscription] = useState<WorkerSubscription | null>(null)

useEffect(() => {
if (!profile?.id) return
const fetchSub = async () => {
const sub = await getActiveWorkerSubscription(profile.id)
setSubscription(sub)
}
fetchSub()
}, [profile])

const handleLogout = async () => {
await supabase.auth.signOut()
Expand Down Expand Up @@ -46,7 +57,8 @@ export default function ProfilePage() {
title: 'الشؤون المالية',
items: [
{ title: 'الأرباح والمحفظة', href: '/worker/wallet', icon: Wallet, color: '#FFB800', bg: '#1A1813' },
{ title: 'طرق الدفع', href: '/worker/profile/payment-methods', icon: CreditCard, color: '#FFB800', bg: '#1A1813' }
{ title: 'طرق الدفع', href: '/worker/profile/payment-methods', icon: CreditCard, color: '#FFB800', bg: '#1A1813' },
{ title: 'باقة الاشتراك', href: '/worker/subscriptions', icon: Star, color: '#3B82F6', bg: '#172554' }
]
},
{
Expand Down Expand Up @@ -85,6 +97,21 @@ export default function ProfilePage() {
/>
</div>

{subscription?.plan_type === 'premium' && (
<div className="px-6 mb-8">
<a href="https://wa.me/201000000000" target="_blank" rel="noreferrer" className="w-full bg-gradient-to-r from-blue-500 to-indigo-600 rounded-2xl p-4 flex items-center justify-between shadow-lg shadow-blue-500/20 relative overflow-hidden group">
<div className="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full blur-2xl group-hover:scale-150 transition-transform duration-500" />
<div className="relative z-10">
<h3 className="text-white font-bold mb-1">خدمة الدعم المتميزة</h3>
<p className="text-blue-100 text-xs">تواصل مع مدير حسابك المخصص</p>
</div>
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center relative z-10 shrink-0">
<Headphones className="text-white" size={20} />
</div>
</a>
</div>
)}

<div className="flex flex-col gap-6 px-6 mb-8">
{menuGroups.map((group, i) => (
<MenuGroup key={i} title={group.title} items={group.items} />
Expand Down
Loading
Loading