File size: 5,175 Bytes
71cad45
 
43c0b25
fe7c6a8
aee79c9
 
 
fe7c6a8
ea4e9d5
aee79c9
 
 
fe7c6a8
0c9fa7f
43c0b25
fe7c6a8
 
 
 
 
 
 
 
aee79c9
 
0c9fa7f
43c0b25
0c9fa7f
 
 
43c0b25
0c9fa7f
 
 
 
43c0b25
0c9fa7f
 
 
 
43c0b25
 
 
 
 
0c9fa7f
71cad45
aee79c9
 
 
 
 
 
 
 
 
 
 
 
 
fe7c6a8
0c9fa7f
fe7c6a8
aee79c9
0c9fa7f
aee79c9
 
 
 
 
0c9fa7f
 
 
 
 
 
 
43c0b25
 
 
 
 
 
 
0c9fa7f
 
 
 
 
 
 
 
aee79c9
 
 
 
0c9fa7f
aee79c9
 
 
 
 
 
ea4e9d5
 
 
 
 
 
 
 
 
 
 
 
 
aee79c9
 
 
ea4e9d5
 
 
 
 
aee79c9
 
 
 
ba9e4dc
d509f53
 
 
ba9e4dc
d509f53
 
 
 
 
 
fa5dee7
d509f53
 
ba9e4dc
aee79c9
71cad45
 
aee79c9
 
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
import ReactDOM from "react-dom";

export type ErrorModalHeadingVariant = "default" | "pipeline" | "maintenance" | "warning";

interface Props {
  open: boolean;
  message: string;
  variant?: ErrorModalHeadingVariant;
  showUpgrade?: boolean;
  onClose: () => void;
}

const PIPELINE_HEADING = "Oops 😢";
const MAINTENANCE_HEADING = "We're updating right now";
const WARNING_HEADING = "Oops 😢";

export default function ErrorModal({
  open,
  message,
  variant = "default",
  showUpgrade,
  onClose,
}: Props) {
  if (!open) return null;

  const isMaintenance = variant === "maintenance";
  const isWarning = variant === "warning";
  const isSoft = variant === "pipeline" || showUpgrade;
  const iconBgClass = isMaintenance
    ? "bg-blue-100"
    : isWarning || isSoft
      ? "bg-amber-100"
      : "bg-red-100";
  const iconColorClass = isMaintenance
    ? "text-blue-600"
    : isWarning || isSoft
      ? "text-amber-700"
      : "text-red-600";
  const heading = isMaintenance
    ? MAINTENANCE_HEADING
    : isWarning
      ? WARNING_HEADING
      : isSoft
        ? PIPELINE_HEADING
        : "Error";

  return ReactDOM.createPortal(
    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
      <div
        className="absolute inset-0 bg-black/40 backdrop-blur-sm"
        onClick={onClose}
        aria-hidden
      />
      <div
        className="relative bg-white border border-gray-200 shadow-2xl rounded-xl max-w-md w-full mx-4 py-6 px-6"
        role="alertdialog"
        aria-modal="true"
        aria-labelledby="error-modal-title"
      >
        <div className="flex items-start gap-4">
          <div
            className={`flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center ${iconBgClass}`}
          >
            <svg
              className={`w-5 h-5 ${iconColorClass}`}
              fill="none"
              stroke="currentColor"
              viewBox="0 0 24 24"
              aria-hidden
            >
              {isMaintenance ? (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
                />
              ) : isWarning ? (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M12 9v2m0 4h.01M4.93 19h14.14c1.54 0 2.5-1.67 1.73-3L13.73 4a2 2 0 00-3.46 0L3.2 16c-.77 1.33.19 3 1.73 3z"
                />
              ) : (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
                />
              )}
            </svg>
          </div>
          <div className="flex-1 min-w-0">
            <h3 id="error-modal-title" className="text-base font-semibold text-gray-900 mb-1">
              {heading}
            </h3>
            <p className="text-sm text-gray-600 whitespace-pre-wrap break-words">
              {message}
            </p>
          </div>
        </div>
        <div className="mt-5 flex justify-end gap-3">
          {showUpgrade && (
            <button
              type="button"
              onClick={() => {
                onClose();
                window.location.href = "/pricing";
              }}
              className="px-4 py-2 text-sm font-medium text-white bg-purple-700 hover:bg-purple-800 rounded-lg transition-colors"
            >
              Upgrade plan
            </button>
          )}
          <button
            type="button"
            onClick={onClose}
            className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
              showUpgrade
                ? "text-gray-700 bg-gray-100 hover:bg-gray-200"
                : "text-white bg-purple-700 hover:bg-purple-800"
            }`}
          >
            OK
          </button>
        </div>
        {/* Share B2V disabled
        {showUpgrade && (
          <div className="mt-4 pt-4 border-t border-gray-100 flex justify-center">
            <a
              href="/survey"
              className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-purple-50 border border-purple-200 text-xs font-medium text-purple-700 hover:bg-purple-100 hover:border-purple-400 transition-all relative"
            >
              <svg xmlns="http://www.w3.org/2000/svg" className="w-3.5 h-3.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <polyline points="20 12 20 22 4 22 4 12" /><rect x="2" y="7" width="20" height="5" /><line x1="12" y1="22" x2="12" y2="7" />
                <path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z" /><path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z" />
              </svg>
              Share B2V to get 5 videos free
            </a>
          </div>
        )} */}
      </div>
    </div>,
    document.body
  );
}