All files / src/components/features/admin/referrals/ReferralSettings index.tsx

0% Statements 0/148
100% Branches 0/0
0% Functions 0/1
0% Lines 0/148

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149                                                                                                                                                                                                                                                                                                         
"use client";

import { useState, useEffect } from "react";

export default function ReferralSettings() {
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState("");

  const [formData, setFormData] = useState({
    referrerRewardType: "points",
    referrerRewardValue: 500,
    refereeRewardType: "discount",
    refereeRewardValue: 10,
    refereeDiscountType: "percentage",
    minPurchase: "",
    isActive: true});

  useEffect(() => { fetchSettings(); }, []);

  const fetchSettings = async () => {
    setLoading(true);
    try {
      const res = await fetch("/api/admin/referrals/settings");
      const data = await res.json();
      if (!res.ok || data.success === false) throw new Error(data.error || "Failed to fetch settings");

      setFormData({
        referrerRewardType: data.data.defaultReferrerReward?.type || "points",
        referrerRewardValue: data.data.defaultReferrerReward?.value || 500,
        refereeRewardType: data.data.defaultRefereeReward?.type || "discount",
        refereeRewardValue: data.data.defaultRefereeReward?.value || 10,
        refereeDiscountType: data.data.defaultRefereeReward?.discountType || "percentage",
        minPurchase: data.data.minPurchase?.toString() || "",
        isActive: data.data.isActive ?? true});
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to fetch settings");
    } finally {
      setLoading(false);
    }
  };

  const handleSave = async () => {
    setSaving(true);
    setError(null);
    setSuccess("");
    try {
      const res = await fetch("/api/admin/referrals/settings", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          defaultReferrerReward: { type: formData.referrerRewardType, value: formData.referrerRewardValue },
          defaultRefereeReward: {
            type: formData.refereeRewardType,
            value: formData.refereeRewardValue,
            discountType: formData.refereeRewardType === "discount" ? formData.refereeDiscountType : undefined},
          minPurchase: formData.minPurchase ? parseFloat(formData.minPurchase) : null,
          isActive: formData.isActive})});

      const data = await res.json();
      if (!res.ok || data.success === false) throw new Error(data.error || "Failed to save settings");

      setSuccess("Settings saved successfully!");
      fetchSettings();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to save settings");
    } finally {
      setSaving(false);
    }
  };

  if (loading) return <div className="flex items-center justify-center h-64"><p className="text-gray-500">Loading settings...</p></div>;

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">Referral Settings</h1>
        <p className="text-gray-600 dark:text-gray-400 mt-1">Configure your referral program defaults</p>
      </div>

      {error && <div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 text-red-700 dark:text-red-400">{error}</div>}
      {success && <div className="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-4 text-green-700 dark:text-green-400">{success}</div>}

      <div className="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-6 space-y-6">
        {/* Program Status */}
        <div className="flex items-center justify-between pb-4 border-b border-gray-200 dark:border-gray-700">
          <div>
            <h3 className="font-medium text-gray-900 dark:text-gray-100">Program Status</h3>
            <p className="text-sm text-gray-500">Enable or disable the referral program</p>
          </div>
          <label className="relative inline-flex items-center cursor-pointer">
            <input type="checkbox" checked={formData.isActive} onChange={(e) => setFormData((prev) => ({ ...prev, isActive: e.target.checked }))} className="sr-only peer" />
            <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div>
          </label>
        </div>

        {/* Referrer Reward */}
        <div>
          <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Default Referrer Reward</label>
          <div className="flex gap-2">
            <select value={formData.referrerRewardType} onChange={(e) => setFormData((prev) => ({ ...prev, referrerRewardType: e.target.value }))} className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
              <option value="points">Points</option>
              <option value="credit">Credit</option>
              <option value="discount">Discount</option>
            </select>
            <input type="number" value={formData.referrerRewardValue} onChange={(e) => setFormData((prev) => ({ ...prev, referrerRewardValue: parseInt(e.target.value) || 0 }))} className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" />
          </div>
          <p className="text-sm text-gray-500 mt-1">Reward given to the person who refers new customers</p>
        </div>

        {/* Referee Reward */}
        <div>
          <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Default Referee Reward</label>
          <div className="flex gap-2">
            <select value={formData.refereeRewardType} onChange={(e) => setFormData((prev) => ({ ...prev, refereeRewardType: e.target.value }))} className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
              <option value="points">Points</option>
              <option value="credit">Credit</option>
              <option value="discount">Discount</option>
            </select>
            <input type="number" value={formData.refereeRewardValue} onChange={(e) => setFormData((prev) => ({ ...prev, refereeRewardValue: parseInt(e.target.value) || 0 }))} className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" />
            {formData.refereeRewardType === "discount" && (
              <select value={formData.refereeDiscountType} onChange={(e) => setFormData((prev) => ({ ...prev, refereeDiscountType: e.target.value }))} className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
                <option value="percentage">%</option>
                <option value="fixed">$</option>
              </select>
            )}
          </div>
          <p className="text-sm text-gray-500 mt-1">Reward given to new customers who use a referral code</p>
        </div>

        {/* Min Purchase */}
        <div>
          <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Minimum Purchase (optional)</label>
          <input type="number" value={formData.minPurchase} onChange={(e) => setFormData((prev) => ({ ...prev, minPurchase: e.target.value }))} placeholder="Leave empty for no minimum" className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" />
          <p className="text-sm text-gray-500 mt-1">Minimum purchase amount required to qualify for rewards</p>
        </div>

        {/* Save Button */}
        <div className="pt-4 border-t border-gray-200 dark:border-gray-700">
          <button onClick={handleSave} disabled={saving} className="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 disabled:opacity-50">
            {saving ? "Saving..." : "Save Settings"}
          </button>
        </div>
      </div>
    </div>
  );
}