@universe-platform/ui 0.1.0-beta.22 → 0.1.0-beta.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/globals.css CHANGED
@@ -1072,6 +1072,9 @@
1072
1072
  .border-emerald-500 {
1073
1073
  border-color: var(--color-emerald-500);
1074
1074
  }
1075
+ .border-emerald-600 {
1076
+ border-color: var(--color-emerald-600);
1077
+ }
1075
1078
  .border-fuchsia-200 {
1076
1079
  border-color: var(--color-fuchsia-200);
1077
1080
  }
package/dist/index.d.mts CHANGED
@@ -53,10 +53,14 @@ declare function isValidPhoneNumber(phone: string): boolean;
53
53
  */
54
54
  declare function generateWhatsAppLink(phone: string, defaultCountryCode?: string): string | null;
55
55
  /**
56
- * Normalizes a phone number for WhatsApp sending (E.164 without leading '+').
57
- * - Strips spaces, dashes, and special characters.
58
- * - If it starts with '05' (10 digits), replaces the leading '0' with '972' (e.g., '0586812807' -> '972586812807').
59
- * - If it starts with '+', strips the '+'.
56
+ * Normalizes a phone number for WhatsApp sending (Meta international format without leading '+').
57
+ * - Returns empty string if input is falsy.
58
+ * - Trims input and removes leading '+' if it exists.
59
+ * - Strips all non-digit characters.
60
+ * - If starting with '972', returns it as is.
61
+ * - If starting with '0' (Israeli mobile/landline), strips '0' and prepends '972'.
62
+ * - If exactly 9 digits long (omitted leading zero), prepends '972'.
63
+ * - Returns the processed digits string.
60
64
  */
61
65
  declare function normalizePhoneForWhatsApp(phone: string): string;
62
66
 
@@ -547,6 +551,8 @@ declare function useWhatsAppState(props: WhatsAppComponentProps): {
547
551
  isTestDisabled: boolean;
548
552
  sendButtonTitle: string;
549
553
  testButtonTitle: string;
554
+ requiresMediaHeader: boolean;
555
+ isMediaMissing: boolean;
550
556
  templates: MetaWhatsAppTemplate[];
551
557
  selectedTemplateId: string | null;
552
558
  setSelectedTemplateId: react.Dispatch<react.SetStateAction<string | null>>;
@@ -556,6 +562,8 @@ declare function useWhatsAppState(props: WhatsAppComponentProps): {
556
562
  headerMediaUrl: string;
557
563
  handleHeaderMediaUrlChange: (newUrl: string) => void;
558
564
  handleResetHeaderMediaUrl: () => void;
565
+ isUploadingMedia: boolean;
566
+ handleFileUpload: (file: File, templateName?: string) => Promise<void>;
559
567
  templateVariables: string[];
560
568
  variableMapping: TemplateVariableMapping;
561
569
  textableColumnOptions: TextableColumnOption[];
package/dist/index.d.ts CHANGED
@@ -53,10 +53,14 @@ declare function isValidPhoneNumber(phone: string): boolean;
53
53
  */
54
54
  declare function generateWhatsAppLink(phone: string, defaultCountryCode?: string): string | null;
55
55
  /**
56
- * Normalizes a phone number for WhatsApp sending (E.164 without leading '+').
57
- * - Strips spaces, dashes, and special characters.
58
- * - If it starts with '05' (10 digits), replaces the leading '0' with '972' (e.g., '0586812807' -> '972586812807').
59
- * - If it starts with '+', strips the '+'.
56
+ * Normalizes a phone number for WhatsApp sending (Meta international format without leading '+').
57
+ * - Returns empty string if input is falsy.
58
+ * - Trims input and removes leading '+' if it exists.
59
+ * - Strips all non-digit characters.
60
+ * - If starting with '972', returns it as is.
61
+ * - If starting with '0' (Israeli mobile/landline), strips '0' and prepends '972'.
62
+ * - If exactly 9 digits long (omitted leading zero), prepends '972'.
63
+ * - Returns the processed digits string.
60
64
  */
61
65
  declare function normalizePhoneForWhatsApp(phone: string): string;
62
66
 
@@ -547,6 +551,8 @@ declare function useWhatsAppState(props: WhatsAppComponentProps): {
547
551
  isTestDisabled: boolean;
548
552
  sendButtonTitle: string;
549
553
  testButtonTitle: string;
554
+ requiresMediaHeader: boolean;
555
+ isMediaMissing: boolean;
550
556
  templates: MetaWhatsAppTemplate[];
551
557
  selectedTemplateId: string | null;
552
558
  setSelectedTemplateId: react.Dispatch<react.SetStateAction<string | null>>;
@@ -556,6 +562,8 @@ declare function useWhatsAppState(props: WhatsAppComponentProps): {
556
562
  headerMediaUrl: string;
557
563
  handleHeaderMediaUrlChange: (newUrl: string) => void;
558
564
  handleResetHeaderMediaUrl: () => void;
565
+ isUploadingMedia: boolean;
566
+ handleFileUpload: (file: File, templateName?: string) => Promise<void>;
559
567
  templateVariables: string[];
560
568
  variableMapping: TemplateVariableMapping;
561
569
  textableColumnOptions: TextableColumnOption[];
package/dist/index.js CHANGED
@@ -202,9 +202,15 @@ function normalizePhoneForWhatsApp(phone) {
202
202
  cleaned = cleaned.substring(1);
203
203
  }
204
204
  const digitsOnly = cleaned.replace(/\D/g, "");
205
- if (digitsOnly.startsWith("05") && digitsOnly.length === 10) {
205
+ if (digitsOnly.startsWith("972")) {
206
+ return digitsOnly;
207
+ }
208
+ if (digitsOnly.startsWith("0")) {
206
209
  return `972${digitsOnly.substring(1)}`;
207
210
  }
211
+ if (digitsOnly.length === 9) {
212
+ return `972${digitsOnly}`;
213
+ }
208
214
  return digitsOnly;
209
215
  }
210
216
 
@@ -9727,9 +9733,12 @@ function buildTemplateComponents(params) {
9727
9733
  const format = (headerComp.format || "TEXT").toUpperCase();
9728
9734
  if (format === "IMAGE" || format === "DOCUMENT" || format === "VIDEO") {
9729
9735
  const validMediaUrl = resolveHeaderMediaUrl(headerComp, mediaUrl, selectedTemplate.name);
9736
+ console.log(
9737
+ `[MEDIA_DEBUG] template: ${selectedTemplate.name}, format: ${format}, resolvedUrl: ${validMediaUrl}`
9738
+ );
9730
9739
  if (validMediaUrl) {
9731
9740
  if (format === "DOCUMENT") {
9732
- const docFilename = resolveDocumentFilename(headerComp, validMediaUrl);
9741
+ const docFilename = resolveDocumentFilename(headerComp, validMediaUrl) || "document.pdf";
9733
9742
  components.push({
9734
9743
  type: "header",
9735
9744
  parameters: [
@@ -9898,11 +9907,6 @@ function findColumnDef(colOption, tableColumns) {
9898
9907
  }
9899
9908
  return null;
9900
9909
  }
9901
- var DEFAULT_FALLBACK_MEDIA_URLS = {
9902
- image: "https://images.unsplash.com/photo-1579202673506-ca3ce28943ef?q=80&w=800&fm=jpg&fit=crop",
9903
- document: "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
9904
- video: "https://www.w3schools.com/html/mov_bbb.mp4"
9905
- };
9906
9910
  function resolveDocumentFilename(headerComp, mediaUrl) {
9907
9911
  var _a, _b, _c;
9908
9912
  if ((_a = headerComp == null ? void 0 : headerComp.document) == null ? void 0 : _a.filename) {
@@ -9916,8 +9920,11 @@ function resolveDocumentFilename(headerComp, mediaUrl) {
9916
9920
  const urlObj = new URL(mediaUrl);
9917
9921
  const pathname = urlObj.pathname;
9918
9922
  const lastSegment = pathname.substring(pathname.lastIndexOf("/") + 1);
9919
- if (lastSegment && lastSegment.toLowerCase().endsWith(".pdf")) {
9920
- return decodeURIComponent(lastSegment);
9923
+ if (lastSegment) {
9924
+ const decoded = decodeURIComponent(lastSegment).trim();
9925
+ if (decoded) {
9926
+ return decoded.includes(".") ? decoded : `${decoded}.pdf`;
9927
+ }
9921
9928
  }
9922
9929
  } catch (e) {
9923
9930
  }
@@ -9925,61 +9932,73 @@ function resolveDocumentFilename(headerComp, mediaUrl) {
9925
9932
  return "document.pdf";
9926
9933
  }
9927
9934
  function resolveHeaderMediaUrl(headerComp, customMediaUrl, templateName) {
9928
- var _a, _b, _c, _d, _e, _f, _g;
9929
- const isImageFormat = ((headerComp == null ? void 0 : headerComp.format) || "").toUpperCase() === "IMAGE";
9935
+ const format = ((headerComp == null ? void 0 : headerComp.format) || "").toUpperCase();
9936
+ const isImageFormat = format === "IMAGE";
9930
9937
  const sanitizeCandidate = (rawUrl) => {
9938
+ if (!rawUrl || typeof rawUrl !== "string") {
9939
+ return null;
9940
+ }
9931
9941
  let trimmed = rawUrl.trim();
9942
+ if (trimmed.startsWith("4:")) {
9943
+ return null;
9944
+ }
9932
9945
  if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://")) {
9933
9946
  return null;
9934
9947
  }
9948
+ const lower = trimmed.toLowerCase();
9949
+ if (lower.includes("scontent.whatsapp.net") || lower.includes("lookaside.fbsbx.com")) {
9950
+ return null;
9951
+ }
9935
9952
  if (isImageFormat) {
9936
- if (trimmed.includes("unsplash.com") && trimmed.includes("auto=format")) {
9937
- trimmed = trimmed.replace("auto=format", "fm=jpg");
9953
+ if (lower.includes("unsplash.com") && lower.includes("auto=format")) {
9954
+ trimmed = trimmed.replace(/auto=format/gi, "fm=jpg");
9938
9955
  }
9939
- if (trimmed.toLowerCase().includes(".webp")) {
9956
+ if (lower.includes(".webp")) {
9940
9957
  return null;
9941
9958
  }
9942
9959
  }
9943
9960
  return trimmed;
9944
9961
  };
9945
- if (customMediaUrl && typeof customMediaUrl === "string") {
9946
- const sanitized = sanitizeCandidate(customMediaUrl);
9947
- if (sanitized) return sanitized;
9948
- }
9949
- if (headerComp) {
9950
- const candidates = [
9951
- headerComp.url,
9952
- headerComp.link,
9953
- (_a = headerComp.image) == null ? void 0 : _a.link,
9954
- (_b = headerComp.document) == null ? void 0 : _b.link,
9955
- (_c = headerComp.video) == null ? void 0 : _c.link,
9956
- (_e = (_d = headerComp.example) == null ? void 0 : _d.header_url) == null ? void 0 : _e[0],
9957
- (_g = (_f = headerComp.example) == null ? void 0 : _f.header_handle) == null ? void 0 : _g[0]
9958
- ];
9959
- for (const candidate of candidates) {
9960
- if (typeof candidate === "string") {
9961
- const sanitized = sanitizeCandidate(candidate);
9962
- if (sanitized) return sanitized;
9962
+ const computeUrl = () => {
9963
+ var _a, _b, _c, _d, _e, _f, _g;
9964
+ if (customMediaUrl && typeof customMediaUrl === "string") {
9965
+ const sanitized = sanitizeCandidate(customMediaUrl);
9966
+ if (sanitized) return sanitized;
9967
+ }
9968
+ if (templateName && typeof window !== "undefined") {
9969
+ try {
9970
+ const persisted = localStorage.getItem(`whatsapp_template_media_${templateName}`) || localStorage.getItem(`whatsapp_template_media_${templateName.toLowerCase()}`);
9971
+ if (persisted && typeof persisted === "string") {
9972
+ const sanitized = sanitizeCandidate(persisted);
9973
+ if (sanitized) return sanitized;
9974
+ }
9975
+ } catch (e) {
9963
9976
  }
9964
9977
  }
9965
- }
9966
- if (templateName && typeof window !== "undefined") {
9967
- try {
9968
- const persisted = localStorage.getItem(`whatsapp_template_media_${templateName}`);
9969
- if (persisted && typeof persisted === "string") {
9970
- const sanitized = sanitizeCandidate(persisted);
9971
- if (sanitized) return sanitized;
9978
+ if (headerComp) {
9979
+ const candidates = [
9980
+ headerComp.url,
9981
+ headerComp.link,
9982
+ (_a = headerComp.image) == null ? void 0 : _a.link,
9983
+ (_b = headerComp.document) == null ? void 0 : _b.link,
9984
+ (_c = headerComp.video) == null ? void 0 : _c.link,
9985
+ (_e = (_d = headerComp.example) == null ? void 0 : _d.header_url) == null ? void 0 : _e[0],
9986
+ (_g = (_f = headerComp.example) == null ? void 0 : _f.header_handle) == null ? void 0 : _g[0]
9987
+ ];
9988
+ for (const candidate of candidates) {
9989
+ if (typeof candidate === "string") {
9990
+ const sanitized = sanitizeCandidate(candidate);
9991
+ if (sanitized) return sanitized;
9992
+ }
9972
9993
  }
9973
- } catch (e) {
9974
9994
  }
9975
- }
9976
- if (headerComp) {
9977
- const format = (headerComp.format || "").toUpperCase();
9978
- if (format === "DOCUMENT") return DEFAULT_FALLBACK_MEDIA_URLS.document;
9979
- if (format === "VIDEO") return DEFAULT_FALLBACK_MEDIA_URLS.video;
9980
- return DEFAULT_FALLBACK_MEDIA_URLS.image;
9981
- }
9982
- return DEFAULT_FALLBACK_MEDIA_URLS.image;
9995
+ return null;
9996
+ };
9997
+ const validMediaUrl = computeUrl();
9998
+ console.log(
9999
+ `[MEDIA_DEBUG] template: ${templateName || "unknown"}, format: ${format}, resolvedUrl: ${validMediaUrl}`
10000
+ );
10001
+ return validMediaUrl;
9983
10002
  }
9984
10003
  function buildWhatsAppPayload(to, templateName, languageCode, components) {
9985
10004
  const payload = {
@@ -11133,6 +11152,48 @@ function useWhatsAppState(props) {
11133
11152
  );
11134
11153
  setHeaderMediaUrl(defaultUrl || "");
11135
11154
  }, [templateHeaderComp, selectedTemplate == null ? void 0 : selectedTemplate.name]);
11155
+ const [isUploadingMedia, setIsUploadingMedia] = (0, import_react17.useState)(false);
11156
+ const handleFileUpload = (0, import_react17.useCallback)(
11157
+ async (file, templateName) => {
11158
+ const tplName = templateName || (selectedTemplate == null ? void 0 : selectedTemplate.name) || "";
11159
+ if (!file) return;
11160
+ setIsUploadingMedia(true);
11161
+ try {
11162
+ const formData = new FormData();
11163
+ formData.append("file", file);
11164
+ const res = await fetch("/api/whatsapp/upload-media", {
11165
+ method: "POST",
11166
+ body: formData
11167
+ });
11168
+ const data = await res.json().catch(() => ({}));
11169
+ if (!res.ok || !(data == null ? void 0 : data.url)) {
11170
+ throw new Error((data == null ? void 0 : data.error) || `\u05D4\u05E2\u05DC\u05D0\u05EA \u05D4\u05E7\u05D5\u05D1\u05E5 \u05E0\u05DB\u05E9\u05DC\u05D4 (${res.status})`);
11171
+ }
11172
+ const publicUrl = data.url;
11173
+ setHeaderMediaUrl(publicUrl);
11174
+ if (tplName && typeof window !== "undefined") {
11175
+ try {
11176
+ localStorage.setItem(`whatsapp_template_media_${tplName}`, publicUrl);
11177
+ localStorage.setItem(`whatsapp_template_media_${tplName.toLowerCase()}`, publicUrl);
11178
+ } catch (e) {
11179
+ }
11180
+ }
11181
+ setFeedback({
11182
+ type: "success",
11183
+ message: "\u05E7\u05D5\u05D1\u05E5 \u05D4\u05DE\u05D3\u05D9\u05D4 \u05D4\u05D5\u05E2\u05DC\u05D4 \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4 \u05D5\u05E9\u05D5\u05D9\u05DA \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA"
11184
+ });
11185
+ } catch (err) {
11186
+ console.error("[useWhatsAppState] handleFileUpload error:", err);
11187
+ setFeedback({
11188
+ type: "error",
11189
+ message: (err == null ? void 0 : err.message) || "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E2\u05EA \u05D4\u05E2\u05DC\u05D0\u05EA \u05E7\u05D5\u05D1\u05E5 \u05D4\u05DE\u05D3\u05D9\u05D4"
11190
+ });
11191
+ } finally {
11192
+ setIsUploadingMedia(false);
11193
+ }
11194
+ },
11195
+ [selectedTemplate == null ? void 0 : selectedTemplate.name]
11196
+ );
11136
11197
  const templateVariables = (0, import_react17.useMemo)(() => {
11137
11198
  return extractTemplateVariables(selectedTemplate);
11138
11199
  }, [selectedTemplate]);
@@ -11250,13 +11311,20 @@ function useWhatsAppState(props) {
11250
11311
  const isTemplateValid = Boolean(
11251
11312
  selectedTemplate && getStatusConfig(selectedTemplate.status).isSelectable
11252
11313
  );
11253
- const isSendDisabled = isSendingBroadcast || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || selectedRowIds.length === 0 || finalRecipientCount === 0 || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing;
11254
- const isTestDisabled = isSendingTest || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing;
11255
- const sendButtonTitle = isSendingBroadcast ? "\u05E9\u05D5\u05DC\u05D7 \u05D4\u05D5\u05D3\u05E2\u05D5\u05EA \u05D1\u05EA\u05E4\u05D5\u05E6\u05D4..." : isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : selectedRowIds.length === 0 ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05DC\u05E4\u05D7\u05D5\u05EA \u05E9\u05D5\u05E8\u05D4 \u05D0\u05D7\u05EA \u05DE\u05D4\u05D8\u05D1\u05DC\u05D4" : finalRecipientCount === 0 ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD \u05E2\u05DD \u05D8\u05DC\u05E4\u05D5\u05DF \u05EA\u05E7\u05D9\u05DF \u05D4\u05EA\u05D5\u05D0\u05DE\u05D9\u05DD \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF" : `\u05E9\u05DC\u05D7 \u05DC-${finalRecipientCount} \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD (\u05DE\u05EA\u05D5\u05DA ${validMainRowsCount} \u05E9\u05D5\u05E8\u05D5\u05EA \u05E8\u05D0\u05E9\u05D9\u05D5\u05EA)`;
11256
- const testButtonTitle = isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : "\u05E9\u05DC\u05D9\u05D7\u05EA \u05D4\u05D5\u05D3\u05E2\u05EA \u05D1\u05D3\u05D9\u05E7\u05D4 \u05DC\u05DE\u05E1\u05E4\u05E8 \u05D1\u05D3\u05D9\u05E7\u05D4";
11314
+ const headerFormat = ((templateHeaderComp == null ? void 0 : templateHeaderComp.format) || "").toUpperCase();
11315
+ const requiresMediaHeader = headerFormat === "IMAGE" || headerFormat === "DOCUMENT" || headerFormat === "VIDEO";
11316
+ const isMediaMissing = requiresMediaHeader && (!headerMediaUrl || !headerMediaUrl.trim() || !headerMediaUrl.startsWith("http"));
11317
+ const isSendDisabled = isSendingBroadcast || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || selectedRowIds.length === 0 || finalRecipientCount === 0 || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing || isMediaMissing;
11318
+ const isTestDisabled = isSendingTest || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing || isMediaMissing;
11319
+ const sendButtonTitle = isSendingBroadcast ? "\u05E9\u05D5\u05DC\u05D7 \u05D4\u05D5\u05D3\u05E2\u05D5\u05EA \u05D1\u05EA\u05E4\u05D5\u05E6\u05D4..." : isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isMediaMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05E2\u05DC\u05D5\u05EA \u05E7\u05D5\u05D1\u05E5 \u05E2\u05D1\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5 \u05DB\u05D3\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : selectedRowIds.length === 0 ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05DC\u05E4\u05D7\u05D5\u05EA \u05E9\u05D5\u05E8\u05D4 \u05D0\u05D7\u05EA \u05DE\u05D4\u05D8\u05D1\u05DC\u05D4" : finalRecipientCount === 0 ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD \u05E2\u05DD \u05D8\u05DC\u05E4\u05D5\u05DF \u05EA\u05E7\u05D9\u05DF \u05D4\u05EA\u05D5\u05D0\u05DE\u05D9\u05DD \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF" : `\u05E9\u05DC\u05D7 \u05DC-${finalRecipientCount} \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD (\u05DE\u05EA\u05D5\u05DA ${validMainRowsCount} \u05E9\u05D5\u05E8\u05D5\u05EA \u05E8\u05D0\u05E9\u05D9\u05D5\u05EA)`;
11320
+ const testButtonTitle = isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isMediaMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05E2\u05DC\u05D5\u05EA \u05E7\u05D5\u05D1\u05E5 \u05E2\u05D1\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5 \u05DB\u05D3\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : "\u05E9\u05DC\u05D9\u05D7\u05EA \u05D4\u05D5\u05D3\u05E2\u05EA \u05D1\u05D3\u05D9\u05E7\u05D4 \u05DC\u05DE\u05E1\u05E4\u05E8 \u05D1\u05D3\u05D9\u05E7\u05D4";
11257
11321
  const handleSendTest = async () => {
11258
11322
  var _a2;
11259
11323
  if (isSendingTest) return;
11324
+ if (isMediaMissing) {
11325
+ setTestFeedback("\u05D9\u05E9 \u05DC\u05D4\u05E2\u05DC\u05D5\u05EA \u05E7\u05D5\u05D1\u05E5 \u05E2\u05D1\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5 \u05DB\u05D3\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7");
11326
+ return;
11327
+ }
11260
11328
  if (isSubtableFilterIncomplete) {
11261
11329
  setTestFeedback("\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA");
11262
11330
  return;
@@ -11598,6 +11666,8 @@ function useWhatsAppState(props) {
11598
11666
  isTestDisabled,
11599
11667
  sendButtonTitle,
11600
11668
  testButtonTitle,
11669
+ requiresMediaHeader,
11670
+ isMediaMissing,
11601
11671
  // Templates & Meta API
11602
11672
  templates,
11603
11673
  selectedTemplateId,
@@ -11608,6 +11678,8 @@ function useWhatsAppState(props) {
11608
11678
  headerMediaUrl,
11609
11679
  handleHeaderMediaUrlChange,
11610
11680
  handleResetHeaderMediaUrl,
11681
+ isUploadingMedia,
11682
+ handleFileUpload,
11611
11683
  templateVariables,
11612
11684
  variableMapping,
11613
11685
  textableColumnOptions,
@@ -11644,6 +11716,7 @@ function useWhatsAppState(props) {
11644
11716
  }
11645
11717
 
11646
11718
  // src/components/WhatsAppComponent/ui/TemplateSelectionBlock.tsx
11719
+ var import_react18 = require("react");
11647
11720
  var import_lucide_react11 = require("lucide-react");
11648
11721
  var import_jsx_runtime15 = require("react/jsx-runtime");
11649
11722
  function TemplateSelectionBlock({
@@ -11667,8 +11740,11 @@ function TemplateSelectionBlock({
11667
11740
  handleDeleteTemplate,
11668
11741
  isDeleting,
11669
11742
  setIsModalOpen,
11670
- setFeedback
11743
+ setFeedback,
11744
+ isUploadingMedia = false,
11745
+ handleFileUpload
11671
11746
  }) {
11747
+ const fileInputRef = (0, import_react18.useRef)(null);
11672
11748
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "col-span-12 lg:col-span-5 min-h-[26rem] h-full flex flex-col bg-slate-50/70 border border-slate-200 rounded-2xl p-3.5 relative", children: [
11673
11749
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "space-y-1.5 shrink-0", children: [
11674
11750
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between pb-1.5 border-b border-slate-200/70 shrink-0 gap-2", children: [
@@ -11848,10 +11924,15 @@ function TemplateSelectionBlock({
11848
11924
  })(),
11849
11925
  templateHeaderComp && (() => {
11850
11926
  const headerFormat = (templateHeaderComp.format || "").toUpperCase();
11927
+ if (headerFormat !== "IMAGE" && headerFormat !== "DOCUMENT" && headerFormat !== "VIDEO") {
11928
+ return null;
11929
+ }
11851
11930
  const docFilename = resolveDocumentFilename(templateHeaderComp, headerMediaUrl);
11931
+ const hasValidMedia = Boolean(headerMediaUrl && headerMediaUrl.trim() && headerMediaUrl.startsWith("http"));
11932
+ const uploadButtonLabel = headerFormat === "DOCUMENT" ? "\u05D4\u05E2\u05DC\u05D4 \u05DE\u05E1\u05DE\u05DA" : headerFormat === "IMAGE" ? "\u05D4\u05E2\u05DC\u05D4 \u05EA\u05DE\u05D5\u05E0\u05D4" : "\u05D4\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5";
11852
11933
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-2.5 bg-slate-50 border border-slate-200/90 rounded-xl flex items-center justify-between gap-3 shrink-0 shadow-2xs", children: [
11853
11934
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center gap-2.5 overflow-hidden", children: headerFormat === "IMAGE" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
11854
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-8 h-8 rounded-lg overflow-hidden border border-slate-200 shrink-0 bg-slate-100 flex items-center justify-center", children: headerMediaUrl && headerMediaUrl.startsWith("http") ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
11935
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-8 h-8 rounded-lg overflow-hidden border border-slate-200 shrink-0 bg-slate-100 flex items-center justify-center", children: hasValidMedia ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
11855
11936
  "img",
11856
11937
  {
11857
11938
  src: headerMediaUrl,
@@ -11861,26 +11942,33 @@ function TemplateSelectionBlock({
11861
11942
  e.currentTarget.style.display = "none";
11862
11943
  }
11863
11944
  }
11864
- ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.Image, { className: "w-4 h-4 text-emerald-600" }) }),
11945
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.Image, { className: "w-4 h-4 text-amber-600" }) }),
11865
11946
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "overflow-hidden", children: [
11866
11947
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs font-bold text-slate-800 block truncate", children: "\u05EA\u05DE\u05D5\u05E0\u05EA \u05DB\u05D5\u05EA\u05E8\u05EA" }),
11867
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] text-slate-400 block truncate", children: "\u05E7\u05D5\u05D1\u05E5 \u05DE\u05D3\u05D9\u05D4 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05D1-Header" })
11948
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] text-slate-400 block truncate", children: hasValidMedia ? "\u05E7\u05D5\u05D1\u05E5 \u05EA\u05DE\u05D5\u05E0\u05D4 \u05DE\u05E7\u05D5\u05E9\u05E8 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA" : "\u05D8\u05E8\u05DD \u05D4\u05D5\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5 \u05EA\u05DE\u05D5\u05E0\u05D4" })
11868
11949
  ] })
11869
11950
  ] }) : headerFormat === "DOCUMENT" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
11870
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-8 h-8 rounded-lg bg-rose-50 text-rose-600 border border-rose-100 flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.FileText, { className: "w-4 h-4" }) }),
11951
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: `w-8 h-8 rounded-lg flex items-center justify-center shrink-0 border ${hasValidMedia ? "bg-rose-50 text-rose-600 border-rose-100" : "bg-slate-100 text-slate-400 border-slate-200"}`, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.FileText, { className: "w-4 h-4" }) }),
11871
11952
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "overflow-hidden", children: [
11872
11953
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs font-bold text-slate-800 block truncate", children: "\u05DE\u05E1\u05DE\u05DA \u05DB\u05D5\u05EA\u05E8\u05EA (PDF)" }),
11873
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] text-slate-500 font-mono block truncate", title: docFilename, children: docFilename })
11954
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] text-slate-500 font-mono block truncate", title: hasValidMedia ? docFilename : void 0, children: hasValidMedia ? docFilename : "\u05D8\u05E8\u05DD \u05D4\u05D5\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5 \u05DE\u05E1\u05DE\u05DA" })
11874
11955
  ] })
11875
11956
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
11876
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-8 h-8 rounded-lg bg-indigo-50 text-indigo-600 border border-indigo-100 flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.Video, { className: "w-4 h-4" }) }),
11957
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: `w-8 h-8 rounded-lg flex items-center justify-center shrink-0 border ${hasValidMedia ? "bg-indigo-50 text-indigo-600 border-indigo-100" : "bg-slate-100 text-slate-400 border-slate-200"}`, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.Video, { className: "w-4 h-4" }) }),
11877
11958
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "overflow-hidden", children: [
11878
11959
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs font-bold text-slate-800 block truncate", children: "\u05E1\u05E8\u05D8\u05D5\u05DF \u05DB\u05D5\u05EA\u05E8\u05EA" }),
11879
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] text-slate-400 block truncate", children: "\u05E7\u05D5\u05D1\u05E5 \u05D5\u05D9\u05D3\u05D0\u05D5 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05D1-Header" })
11960
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] text-slate-400 block truncate", children: hasValidMedia ? "\u05E7\u05D5\u05D1\u05E5 \u05D5\u05D9\u05D3\u05D0\u05D5 \u05DE\u05E7\u05D5\u05E9\u05E8 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA" : "\u05D8\u05E8\u05DD \u05D4\u05D5\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5 \u05D5\u05D9\u05D3\u05D0\u05D5" })
11880
11961
  ] })
11881
11962
  ] }) }),
11882
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1.5 shrink-0", children: [
11883
- headerMediaUrl && headerMediaUrl.startsWith("http") && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
11963
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
11964
+ hasValidMedia ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "inline-flex items-center gap-1 text-[10px] font-medium text-emerald-800 px-2 py-0.5 bg-emerald-50 border border-emerald-200 rounded-lg", children: [
11965
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.CheckCircle2, { className: "w-3 h-3 text-emerald-600" }),
11966
+ "\u05E7\u05D5\u05D1\u05E5 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05EA\u05E7\u05D9\u05DF"
11967
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "inline-flex items-center gap-1 text-[10px] font-semibold text-amber-800 px-2 py-0.5 bg-amber-50 border border-amber-200 rounded-lg", children: [
11968
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.AlertCircle, { className: "w-3 h-3 text-amber-600 shrink-0" }),
11969
+ "\u05E0\u05D3\u05E8\u05E9 \u05E6\u05D9\u05E8\u05D5\u05E3 \u05E7\u05D5\u05D1\u05E5 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5"
11970
+ ] }),
11971
+ hasValidMedia && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
11884
11972
  "a",
11885
11973
  {
11886
11974
  href: headerMediaUrl,
@@ -11891,7 +11979,44 @@ function TemplateSelectionBlock({
11891
11979
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.ExternalLink, { className: "w-3.5 h-3.5" })
11892
11980
  }
11893
11981
  ),
11894
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[9px] text-emerald-800 font-semibold px-1.5 py-0.5 bg-emerald-50 border border-emerald-200 rounded-md", children: "\u05DE\u05E6\u05D5\u05E8\u05E3 \u05D0\u05D5\u05D8\u05D5\u05DE\u05D8\u05D9\u05EA" })
11982
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
11983
+ "input",
11984
+ {
11985
+ ref: fileInputRef,
11986
+ type: "file",
11987
+ className: "hidden",
11988
+ accept: headerFormat === "DOCUMENT" ? ".pdf,application/pdf" : headerFormat === "VIDEO" ? "video/mp4,video/*" : "image/png,image/jpeg,image/jpg",
11989
+ onChange: (e) => {
11990
+ var _a;
11991
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
11992
+ if (file && handleFileUpload && (selectedTemplate == null ? void 0 : selectedTemplate.name)) {
11993
+ handleFileUpload(file, selectedTemplate.name);
11994
+ }
11995
+ e.target.value = "";
11996
+ },
11997
+ disabled: isUploadingMedia
11998
+ }
11999
+ ),
12000
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
12001
+ "button",
12002
+ {
12003
+ type: "button",
12004
+ disabled: isUploadingMedia,
12005
+ onClick: () => {
12006
+ var _a;
12007
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
12008
+ },
12009
+ className: `inline-flex items-center gap-1.5 px-2.5 py-1 text-[11px] font-medium rounded-lg transition-colors border shadow-2xs ${isUploadingMedia ? "bg-slate-100 text-slate-400 border-slate-200 cursor-not-allowed" : hasValidMedia ? "bg-white text-slate-700 hover:bg-slate-50 border-slate-200 hover:border-slate-300" : "bg-emerald-600 text-white hover:bg-emerald-700 border-emerald-600"}`,
12010
+ title: hasValidMedia ? "\u05D4\u05D7\u05DC\u05E3 \u05E7\u05D5\u05D1\u05E5 \u05DE\u05D3\u05D9\u05D4 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5" : `${uploadButtonLabel} \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5`,
12011
+ children: isUploadingMedia ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
12012
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.Loader2, { className: "w-3.5 h-3.5 animate-spin" }),
12013
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "\u05DE\u05E2\u05DC\u05D4..." })
12014
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
12015
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.Upload, { className: "w-3.5 h-3.5" }),
12016
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: hasValidMedia ? "\u05D4\u05D7\u05DC\u05E3 \u05E7\u05D5\u05D1\u05E5" : uploadButtonLabel })
12017
+ ] })
12018
+ }
12019
+ )
11895
12020
  ] })
11896
12021
  ] });
11897
12022
  })(),
@@ -12647,7 +12772,9 @@ function WhatsAppComponent(props) {
12647
12772
  handleDeleteTemplate: state.handleDeleteTemplate,
12648
12773
  isDeleting: state.isDeleting,
12649
12774
  setIsModalOpen: state.setIsModalOpen,
12650
- setFeedback: state.setFeedback
12775
+ setFeedback: state.setFeedback,
12776
+ isUploadingMedia: state.isUploadingMedia,
12777
+ handleFileUpload: state.handleFileUpload
12651
12778
  }
12652
12779
  ),
12653
12780
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
@@ -12833,7 +12960,7 @@ function WhatsAppComponent(props) {
12833
12960
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "w-7 h-7 rounded-md bg-indigo-50 text-indigo-600 border border-indigo-100 flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react14.Video, { className: "w-3.5 h-3.5" }) }),
12834
12961
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-semibold text-slate-800 truncate", children: "\u05E1\u05E8\u05D8\u05D5\u05DF \u05DB\u05D5\u05EA\u05E8\u05EA" })
12835
12962
  ] }) }),
12836
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[10px] text-emerald-700 bg-emerald-50 border border-emerald-200 px-1.5 py-0.5 rounded font-medium shrink-0", children: "\u05DE\u05E6\u05D5\u05E8\u05E3 \u05D0\u05D5\u05D8\u05D5\u05DE\u05D8\u05D9\u05EA" })
12963
+ state.headerMediaUrl && state.headerMediaUrl.startsWith("http") ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[10px] text-emerald-700 bg-emerald-50 border border-emerald-200 px-1.5 py-0.5 rounded font-medium shrink-0", children: "\u05E7\u05D5\u05D1\u05E5 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05EA\u05E7\u05D9\u05DF" }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[10px] text-amber-700 bg-amber-50 border border-amber-200 px-1.5 py-0.5 rounded font-medium shrink-0", children: "\u05E0\u05D3\u05E8\u05E9 \u05E6\u05D9\u05E8\u05D5\u05E3 \u05E7\u05D5\u05D1\u05E5 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5" })
12837
12964
  ] });
12838
12965
  })(),
12839
12966
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-1.5", children: [
@@ -12892,9 +13019,10 @@ function WhatsAppComponent(props) {
12892
13019
  "button",
12893
13020
  {
12894
13021
  type: "button",
12895
- disabled: !state.testPhoneNumber.trim() || state.isSendingTest,
13022
+ disabled: !state.testPhoneNumber.trim() || state.isSendingTest || state.isTestDisabled,
12896
13023
  onClick: state.handleSendTest,
12897
- className: `px-4 py-2 text-xs font-bold rounded-xl transition-all flex items-center gap-1.5 ${!state.testPhoneNumber.trim() || state.isSendingTest ? "bg-emerald-600/40 text-white/70 cursor-not-allowed" : "bg-emerald-600 hover:bg-emerald-700 active:scale-[0.98] text-white shadow-xs cursor-pointer"}`,
13024
+ title: state.testButtonTitle,
13025
+ className: `px-4 py-2 text-xs font-bold rounded-xl transition-all flex items-center gap-1.5 ${!state.testPhoneNumber.trim() || state.isSendingTest || state.isTestDisabled ? "bg-emerald-600/40 text-white/70 cursor-not-allowed" : "bg-emerald-600 hover:bg-emerald-700 active:scale-[0.98] text-white shadow-xs cursor-pointer"}`,
12898
13026
  children: state.isSendingTest ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
12899
13027
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react14.Loader2, { className: "w-3.5 h-3.5 animate-spin" }),
12900
13028
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "\u05E9\u05D5\u05DC\u05D7..." })
package/dist/index.mjs CHANGED
@@ -97,9 +97,15 @@ function normalizePhoneForWhatsApp(phone) {
97
97
  cleaned = cleaned.substring(1);
98
98
  }
99
99
  const digitsOnly = cleaned.replace(/\D/g, "");
100
- if (digitsOnly.startsWith("05") && digitsOnly.length === 10) {
100
+ if (digitsOnly.startsWith("972")) {
101
+ return digitsOnly;
102
+ }
103
+ if (digitsOnly.startsWith("0")) {
101
104
  return `972${digitsOnly.substring(1)}`;
102
105
  }
106
+ if (digitsOnly.length === 9) {
107
+ return `972${digitsOnly}`;
108
+ }
103
109
  return digitsOnly;
104
110
  }
105
111
 
@@ -7713,7 +7719,7 @@ function TableBlock(props) {
7713
7719
  }
7714
7720
 
7715
7721
  // src/components/WhatsAppComponent/WhatsAppComponent.tsx
7716
- import { CheckCircle2 as CheckCircle23, AlertCircle as AlertCircle4, X as X12, Loader2 as Loader27, Phone as Phone4, Send as Send2, Image as ImageIcon3, FileText as FileText4, Video as Video3 } from "lucide-react";
7722
+ import { CheckCircle2 as CheckCircle24, AlertCircle as AlertCircle5, X as X12, Loader2 as Loader27, Phone as Phone4, Send as Send2, Image as ImageIcon3, FileText as FileText4, Video as Video3 } from "lucide-react";
7717
7723
 
7718
7724
  // src/components/WhatsAppComponent/CreateTemplateModal.tsx
7719
7725
  import { useState as useState14, useEffect as useEffect12, useRef as useRef11, useMemo as useMemo7 } from "react";
@@ -9645,9 +9651,12 @@ function buildTemplateComponents(params) {
9645
9651
  const format = (headerComp.format || "TEXT").toUpperCase();
9646
9652
  if (format === "IMAGE" || format === "DOCUMENT" || format === "VIDEO") {
9647
9653
  const validMediaUrl = resolveHeaderMediaUrl(headerComp, mediaUrl, selectedTemplate.name);
9654
+ console.log(
9655
+ `[MEDIA_DEBUG] template: ${selectedTemplate.name}, format: ${format}, resolvedUrl: ${validMediaUrl}`
9656
+ );
9648
9657
  if (validMediaUrl) {
9649
9658
  if (format === "DOCUMENT") {
9650
- const docFilename = resolveDocumentFilename(headerComp, validMediaUrl);
9659
+ const docFilename = resolveDocumentFilename(headerComp, validMediaUrl) || "document.pdf";
9651
9660
  components.push({
9652
9661
  type: "header",
9653
9662
  parameters: [
@@ -9816,11 +9825,6 @@ function findColumnDef(colOption, tableColumns) {
9816
9825
  }
9817
9826
  return null;
9818
9827
  }
9819
- var DEFAULT_FALLBACK_MEDIA_URLS = {
9820
- image: "https://images.unsplash.com/photo-1579202673506-ca3ce28943ef?q=80&w=800&fm=jpg&fit=crop",
9821
- document: "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
9822
- video: "https://www.w3schools.com/html/mov_bbb.mp4"
9823
- };
9824
9828
  function resolveDocumentFilename(headerComp, mediaUrl) {
9825
9829
  var _a, _b, _c;
9826
9830
  if ((_a = headerComp == null ? void 0 : headerComp.document) == null ? void 0 : _a.filename) {
@@ -9834,8 +9838,11 @@ function resolveDocumentFilename(headerComp, mediaUrl) {
9834
9838
  const urlObj = new URL(mediaUrl);
9835
9839
  const pathname = urlObj.pathname;
9836
9840
  const lastSegment = pathname.substring(pathname.lastIndexOf("/") + 1);
9837
- if (lastSegment && lastSegment.toLowerCase().endsWith(".pdf")) {
9838
- return decodeURIComponent(lastSegment);
9841
+ if (lastSegment) {
9842
+ const decoded = decodeURIComponent(lastSegment).trim();
9843
+ if (decoded) {
9844
+ return decoded.includes(".") ? decoded : `${decoded}.pdf`;
9845
+ }
9839
9846
  }
9840
9847
  } catch (e) {
9841
9848
  }
@@ -9843,61 +9850,73 @@ function resolveDocumentFilename(headerComp, mediaUrl) {
9843
9850
  return "document.pdf";
9844
9851
  }
9845
9852
  function resolveHeaderMediaUrl(headerComp, customMediaUrl, templateName) {
9846
- var _a, _b, _c, _d, _e, _f, _g;
9847
- const isImageFormat = ((headerComp == null ? void 0 : headerComp.format) || "").toUpperCase() === "IMAGE";
9853
+ const format = ((headerComp == null ? void 0 : headerComp.format) || "").toUpperCase();
9854
+ const isImageFormat = format === "IMAGE";
9848
9855
  const sanitizeCandidate = (rawUrl) => {
9856
+ if (!rawUrl || typeof rawUrl !== "string") {
9857
+ return null;
9858
+ }
9849
9859
  let trimmed = rawUrl.trim();
9860
+ if (trimmed.startsWith("4:")) {
9861
+ return null;
9862
+ }
9850
9863
  if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://")) {
9851
9864
  return null;
9852
9865
  }
9866
+ const lower = trimmed.toLowerCase();
9867
+ if (lower.includes("scontent.whatsapp.net") || lower.includes("lookaside.fbsbx.com")) {
9868
+ return null;
9869
+ }
9853
9870
  if (isImageFormat) {
9854
- if (trimmed.includes("unsplash.com") && trimmed.includes("auto=format")) {
9855
- trimmed = trimmed.replace("auto=format", "fm=jpg");
9871
+ if (lower.includes("unsplash.com") && lower.includes("auto=format")) {
9872
+ trimmed = trimmed.replace(/auto=format/gi, "fm=jpg");
9856
9873
  }
9857
- if (trimmed.toLowerCase().includes(".webp")) {
9874
+ if (lower.includes(".webp")) {
9858
9875
  return null;
9859
9876
  }
9860
9877
  }
9861
9878
  return trimmed;
9862
9879
  };
9863
- if (customMediaUrl && typeof customMediaUrl === "string") {
9864
- const sanitized = sanitizeCandidate(customMediaUrl);
9865
- if (sanitized) return sanitized;
9866
- }
9867
- if (headerComp) {
9868
- const candidates = [
9869
- headerComp.url,
9870
- headerComp.link,
9871
- (_a = headerComp.image) == null ? void 0 : _a.link,
9872
- (_b = headerComp.document) == null ? void 0 : _b.link,
9873
- (_c = headerComp.video) == null ? void 0 : _c.link,
9874
- (_e = (_d = headerComp.example) == null ? void 0 : _d.header_url) == null ? void 0 : _e[0],
9875
- (_g = (_f = headerComp.example) == null ? void 0 : _f.header_handle) == null ? void 0 : _g[0]
9876
- ];
9877
- for (const candidate of candidates) {
9878
- if (typeof candidate === "string") {
9879
- const sanitized = sanitizeCandidate(candidate);
9880
- if (sanitized) return sanitized;
9880
+ const computeUrl = () => {
9881
+ var _a, _b, _c, _d, _e, _f, _g;
9882
+ if (customMediaUrl && typeof customMediaUrl === "string") {
9883
+ const sanitized = sanitizeCandidate(customMediaUrl);
9884
+ if (sanitized) return sanitized;
9885
+ }
9886
+ if (templateName && typeof window !== "undefined") {
9887
+ try {
9888
+ const persisted = localStorage.getItem(`whatsapp_template_media_${templateName}`) || localStorage.getItem(`whatsapp_template_media_${templateName.toLowerCase()}`);
9889
+ if (persisted && typeof persisted === "string") {
9890
+ const sanitized = sanitizeCandidate(persisted);
9891
+ if (sanitized) return sanitized;
9892
+ }
9893
+ } catch (e) {
9881
9894
  }
9882
9895
  }
9883
- }
9884
- if (templateName && typeof window !== "undefined") {
9885
- try {
9886
- const persisted = localStorage.getItem(`whatsapp_template_media_${templateName}`);
9887
- if (persisted && typeof persisted === "string") {
9888
- const sanitized = sanitizeCandidate(persisted);
9889
- if (sanitized) return sanitized;
9896
+ if (headerComp) {
9897
+ const candidates = [
9898
+ headerComp.url,
9899
+ headerComp.link,
9900
+ (_a = headerComp.image) == null ? void 0 : _a.link,
9901
+ (_b = headerComp.document) == null ? void 0 : _b.link,
9902
+ (_c = headerComp.video) == null ? void 0 : _c.link,
9903
+ (_e = (_d = headerComp.example) == null ? void 0 : _d.header_url) == null ? void 0 : _e[0],
9904
+ (_g = (_f = headerComp.example) == null ? void 0 : _f.header_handle) == null ? void 0 : _g[0]
9905
+ ];
9906
+ for (const candidate of candidates) {
9907
+ if (typeof candidate === "string") {
9908
+ const sanitized = sanitizeCandidate(candidate);
9909
+ if (sanitized) return sanitized;
9910
+ }
9890
9911
  }
9891
- } catch (e) {
9892
9912
  }
9893
- }
9894
- if (headerComp) {
9895
- const format = (headerComp.format || "").toUpperCase();
9896
- if (format === "DOCUMENT") return DEFAULT_FALLBACK_MEDIA_URLS.document;
9897
- if (format === "VIDEO") return DEFAULT_FALLBACK_MEDIA_URLS.video;
9898
- return DEFAULT_FALLBACK_MEDIA_URLS.image;
9899
- }
9900
- return DEFAULT_FALLBACK_MEDIA_URLS.image;
9913
+ return null;
9914
+ };
9915
+ const validMediaUrl = computeUrl();
9916
+ console.log(
9917
+ `[MEDIA_DEBUG] template: ${templateName || "unknown"}, format: ${format}, resolvedUrl: ${validMediaUrl}`
9918
+ );
9919
+ return validMediaUrl;
9901
9920
  }
9902
9921
  function buildWhatsAppPayload(to, templateName, languageCode, components) {
9903
9922
  const payload = {
@@ -11051,6 +11070,48 @@ function useWhatsAppState(props) {
11051
11070
  );
11052
11071
  setHeaderMediaUrl(defaultUrl || "");
11053
11072
  }, [templateHeaderComp, selectedTemplate == null ? void 0 : selectedTemplate.name]);
11073
+ const [isUploadingMedia, setIsUploadingMedia] = useState15(false);
11074
+ const handleFileUpload = useCallback12(
11075
+ async (file, templateName) => {
11076
+ const tplName = templateName || (selectedTemplate == null ? void 0 : selectedTemplate.name) || "";
11077
+ if (!file) return;
11078
+ setIsUploadingMedia(true);
11079
+ try {
11080
+ const formData = new FormData();
11081
+ formData.append("file", file);
11082
+ const res = await fetch("/api/whatsapp/upload-media", {
11083
+ method: "POST",
11084
+ body: formData
11085
+ });
11086
+ const data = await res.json().catch(() => ({}));
11087
+ if (!res.ok || !(data == null ? void 0 : data.url)) {
11088
+ throw new Error((data == null ? void 0 : data.error) || `\u05D4\u05E2\u05DC\u05D0\u05EA \u05D4\u05E7\u05D5\u05D1\u05E5 \u05E0\u05DB\u05E9\u05DC\u05D4 (${res.status})`);
11089
+ }
11090
+ const publicUrl = data.url;
11091
+ setHeaderMediaUrl(publicUrl);
11092
+ if (tplName && typeof window !== "undefined") {
11093
+ try {
11094
+ localStorage.setItem(`whatsapp_template_media_${tplName}`, publicUrl);
11095
+ localStorage.setItem(`whatsapp_template_media_${tplName.toLowerCase()}`, publicUrl);
11096
+ } catch (e) {
11097
+ }
11098
+ }
11099
+ setFeedback({
11100
+ type: "success",
11101
+ message: "\u05E7\u05D5\u05D1\u05E5 \u05D4\u05DE\u05D3\u05D9\u05D4 \u05D4\u05D5\u05E2\u05DC\u05D4 \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4 \u05D5\u05E9\u05D5\u05D9\u05DA \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA"
11102
+ });
11103
+ } catch (err) {
11104
+ console.error("[useWhatsAppState] handleFileUpload error:", err);
11105
+ setFeedback({
11106
+ type: "error",
11107
+ message: (err == null ? void 0 : err.message) || "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E2\u05EA \u05D4\u05E2\u05DC\u05D0\u05EA \u05E7\u05D5\u05D1\u05E5 \u05D4\u05DE\u05D3\u05D9\u05D4"
11108
+ });
11109
+ } finally {
11110
+ setIsUploadingMedia(false);
11111
+ }
11112
+ },
11113
+ [selectedTemplate == null ? void 0 : selectedTemplate.name]
11114
+ );
11054
11115
  const templateVariables = useMemo8(() => {
11055
11116
  return extractTemplateVariables(selectedTemplate);
11056
11117
  }, [selectedTemplate]);
@@ -11168,13 +11229,20 @@ function useWhatsAppState(props) {
11168
11229
  const isTemplateValid = Boolean(
11169
11230
  selectedTemplate && getStatusConfig(selectedTemplate.status).isSelectable
11170
11231
  );
11171
- const isSendDisabled = isSendingBroadcast || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || selectedRowIds.length === 0 || finalRecipientCount === 0 || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing;
11172
- const isTestDisabled = isSendingTest || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing;
11173
- const sendButtonTitle = isSendingBroadcast ? "\u05E9\u05D5\u05DC\u05D7 \u05D4\u05D5\u05D3\u05E2\u05D5\u05EA \u05D1\u05EA\u05E4\u05D5\u05E6\u05D4..." : isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : selectedRowIds.length === 0 ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05DC\u05E4\u05D7\u05D5\u05EA \u05E9\u05D5\u05E8\u05D4 \u05D0\u05D7\u05EA \u05DE\u05D4\u05D8\u05D1\u05DC\u05D4" : finalRecipientCount === 0 ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD \u05E2\u05DD \u05D8\u05DC\u05E4\u05D5\u05DF \u05EA\u05E7\u05D9\u05DF \u05D4\u05EA\u05D5\u05D0\u05DE\u05D9\u05DD \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF" : `\u05E9\u05DC\u05D7 \u05DC-${finalRecipientCount} \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD (\u05DE\u05EA\u05D5\u05DA ${validMainRowsCount} \u05E9\u05D5\u05E8\u05D5\u05EA \u05E8\u05D0\u05E9\u05D9\u05D5\u05EA)`;
11174
- const testButtonTitle = isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : "\u05E9\u05DC\u05D9\u05D7\u05EA \u05D4\u05D5\u05D3\u05E2\u05EA \u05D1\u05D3\u05D9\u05E7\u05D4 \u05DC\u05DE\u05E1\u05E4\u05E8 \u05D1\u05D3\u05D9\u05E7\u05D4";
11232
+ const headerFormat = ((templateHeaderComp == null ? void 0 : templateHeaderComp.format) || "").toUpperCase();
11233
+ const requiresMediaHeader = headerFormat === "IMAGE" || headerFormat === "DOCUMENT" || headerFormat === "VIDEO";
11234
+ const isMediaMissing = requiresMediaHeader && (!headerMediaUrl || !headerMediaUrl.trim() || !headerMediaUrl.startsWith("http"));
11235
+ const isSendDisabled = isSendingBroadcast || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || selectedRowIds.length === 0 || finalRecipientCount === 0 || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing || isMediaMissing;
11236
+ const isTestDisabled = isSendingTest || isScanningPhoneColumns || !hasPhoneColumn || !isTemplateValid || isSubtableFilterIncomplete || isPostSendActionIncomplete || isStaticVariableMissing || isMediaMissing;
11237
+ const sendButtonTitle = isSendingBroadcast ? "\u05E9\u05D5\u05DC\u05D7 \u05D4\u05D5\u05D3\u05E2\u05D5\u05EA \u05D1\u05EA\u05E4\u05D5\u05E6\u05D4..." : isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isMediaMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05E2\u05DC\u05D5\u05EA \u05E7\u05D5\u05D1\u05E5 \u05E2\u05D1\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5 \u05DB\u05D3\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : selectedRowIds.length === 0 ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05DC\u05E4\u05D7\u05D5\u05EA \u05E9\u05D5\u05E8\u05D4 \u05D0\u05D7\u05EA \u05DE\u05D4\u05D8\u05D1\u05DC\u05D4" : finalRecipientCount === 0 ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD \u05E2\u05DD \u05D8\u05DC\u05E4\u05D5\u05DF \u05EA\u05E7\u05D9\u05DF \u05D4\u05EA\u05D5\u05D0\u05DE\u05D9\u05DD \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF" : `\u05E9\u05DC\u05D7 \u05DC-${finalRecipientCount} \u05E0\u05DE\u05E2\u05E0\u05D9\u05DD (\u05DE\u05EA\u05D5\u05DA ${validMainRowsCount} \u05E9\u05D5\u05E8\u05D5\u05EA \u05E8\u05D0\u05E9\u05D9\u05D5\u05EA)`;
11238
+ const testButtonTitle = isScanningPhoneColumns ? "\u05D8\u05D5\u05E2\u05DF \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF..." : !hasPhoneColumn ? "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" : !isTemplateValid ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05DE\u05D0\u05D5\u05E9\u05E8\u05EA \u05EA\u05D7\u05D9\u05DC\u05D4" : isMediaMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05E2\u05DC\u05D5\u05EA \u05E7\u05D5\u05D1\u05E5 \u05E2\u05D1\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5 \u05DB\u05D3\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7" : isSubtableFilterIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA" : isPostSendActionIncomplete ? "\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05D9\u05E2\u05D3 \u05DC\u05E2\u05D3\u05DB\u05D5\u05DF \u05DC\u05D0\u05D7\u05E8 \u05E9\u05DC\u05D9\u05D7\u05D4" : isStaticVariableMissing ? "\u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05E2\u05E8\u05DA \u05E2\u05D1\u05D5\u05E8 \u05DB\u05DC \u05DE\u05E9\u05EA\u05E0\u05D4 \u05D1\u05D8\u05E7\u05E1\u05D8 \u05E7\u05D1\u05D5\u05E2" : "\u05E9\u05DC\u05D9\u05D7\u05EA \u05D4\u05D5\u05D3\u05E2\u05EA \u05D1\u05D3\u05D9\u05E7\u05D4 \u05DC\u05DE\u05E1\u05E4\u05E8 \u05D1\u05D3\u05D9\u05E7\u05D4";
11175
11239
  const handleSendTest = async () => {
11176
11240
  var _a2;
11177
11241
  if (isSendingTest) return;
11242
+ if (isMediaMissing) {
11243
+ setTestFeedback("\u05D9\u05E9 \u05DC\u05D4\u05E2\u05DC\u05D5\u05EA \u05E7\u05D5\u05D1\u05E5 \u05E2\u05D1\u05D5\u05E8 \u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5 \u05DB\u05D3\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7");
11244
+ return;
11245
+ }
11178
11246
  if (isSubtableFilterIncomplete) {
11179
11247
  setTestFeedback("\u05D9\u05E9 \u05DC\u05D1\u05D7\u05D5\u05E8 \u05E2\u05E8\u05DA \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D4\u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA");
11180
11248
  return;
@@ -11516,6 +11584,8 @@ function useWhatsAppState(props) {
11516
11584
  isTestDisabled,
11517
11585
  sendButtonTitle,
11518
11586
  testButtonTitle,
11587
+ requiresMediaHeader,
11588
+ isMediaMissing,
11519
11589
  // Templates & Meta API
11520
11590
  templates,
11521
11591
  selectedTemplateId,
@@ -11526,6 +11596,8 @@ function useWhatsAppState(props) {
11526
11596
  headerMediaUrl,
11527
11597
  handleHeaderMediaUrlChange,
11528
11598
  handleResetHeaderMediaUrl,
11599
+ isUploadingMedia,
11600
+ handleFileUpload,
11529
11601
  templateVariables,
11530
11602
  variableMapping,
11531
11603
  textableColumnOptions,
@@ -11562,6 +11634,7 @@ function useWhatsAppState(props) {
11562
11634
  }
11563
11635
 
11564
11636
  // src/components/WhatsAppComponent/ui/TemplateSelectionBlock.tsx
11637
+ import { useRef as useRef13 } from "react";
11565
11638
  import {
11566
11639
  FileText as FileText3,
11567
11640
  Trash2 as Trash23,
@@ -11573,7 +11646,10 @@ import {
11573
11646
  Video as Video2,
11574
11647
  Phone as Phone2,
11575
11648
  Copy as Copy4,
11576
- MessageSquare as MessageSquare2
11649
+ MessageSquare as MessageSquare2,
11650
+ Upload as Upload3,
11651
+ CheckCircle2 as CheckCircle23,
11652
+ AlertCircle as AlertCircle3
11577
11653
  } from "lucide-react";
11578
11654
  import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
11579
11655
  function TemplateSelectionBlock({
@@ -11597,8 +11673,11 @@ function TemplateSelectionBlock({
11597
11673
  handleDeleteTemplate,
11598
11674
  isDeleting,
11599
11675
  setIsModalOpen,
11600
- setFeedback
11676
+ setFeedback,
11677
+ isUploadingMedia = false,
11678
+ handleFileUpload
11601
11679
  }) {
11680
+ const fileInputRef = useRef13(null);
11602
11681
  return /* @__PURE__ */ jsxs14("div", { className: "col-span-12 lg:col-span-5 min-h-[26rem] h-full flex flex-col bg-slate-50/70 border border-slate-200 rounded-2xl p-3.5 relative", children: [
11603
11682
  /* @__PURE__ */ jsxs14("div", { className: "space-y-1.5 shrink-0", children: [
11604
11683
  /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between pb-1.5 border-b border-slate-200/70 shrink-0 gap-2", children: [
@@ -11778,10 +11857,15 @@ function TemplateSelectionBlock({
11778
11857
  })(),
11779
11858
  templateHeaderComp && (() => {
11780
11859
  const headerFormat = (templateHeaderComp.format || "").toUpperCase();
11860
+ if (headerFormat !== "IMAGE" && headerFormat !== "DOCUMENT" && headerFormat !== "VIDEO") {
11861
+ return null;
11862
+ }
11781
11863
  const docFilename = resolveDocumentFilename(templateHeaderComp, headerMediaUrl);
11864
+ const hasValidMedia = Boolean(headerMediaUrl && headerMediaUrl.trim() && headerMediaUrl.startsWith("http"));
11865
+ const uploadButtonLabel = headerFormat === "DOCUMENT" ? "\u05D4\u05E2\u05DC\u05D4 \u05DE\u05E1\u05DE\u05DA" : headerFormat === "IMAGE" ? "\u05D4\u05E2\u05DC\u05D4 \u05EA\u05DE\u05D5\u05E0\u05D4" : "\u05D4\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5";
11782
11866
  return /* @__PURE__ */ jsxs14("div", { className: "p-2.5 bg-slate-50 border border-slate-200/90 rounded-xl flex items-center justify-between gap-3 shrink-0 shadow-2xs", children: [
11783
11867
  /* @__PURE__ */ jsx15("div", { className: "flex items-center gap-2.5 overflow-hidden", children: headerFormat === "IMAGE" ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
11784
- /* @__PURE__ */ jsx15("div", { className: "w-8 h-8 rounded-lg overflow-hidden border border-slate-200 shrink-0 bg-slate-100 flex items-center justify-center", children: headerMediaUrl && headerMediaUrl.startsWith("http") ? /* @__PURE__ */ jsx15(
11868
+ /* @__PURE__ */ jsx15("div", { className: "w-8 h-8 rounded-lg overflow-hidden border border-slate-200 shrink-0 bg-slate-100 flex items-center justify-center", children: hasValidMedia ? /* @__PURE__ */ jsx15(
11785
11869
  "img",
11786
11870
  {
11787
11871
  src: headerMediaUrl,
@@ -11791,26 +11875,33 @@ function TemplateSelectionBlock({
11791
11875
  e.currentTarget.style.display = "none";
11792
11876
  }
11793
11877
  }
11794
- ) : /* @__PURE__ */ jsx15(ImageIcon2, { className: "w-4 h-4 text-emerald-600" }) }),
11878
+ ) : /* @__PURE__ */ jsx15(ImageIcon2, { className: "w-4 h-4 text-amber-600" }) }),
11795
11879
  /* @__PURE__ */ jsxs14("div", { className: "overflow-hidden", children: [
11796
11880
  /* @__PURE__ */ jsx15("span", { className: "text-xs font-bold text-slate-800 block truncate", children: "\u05EA\u05DE\u05D5\u05E0\u05EA \u05DB\u05D5\u05EA\u05E8\u05EA" }),
11797
- /* @__PURE__ */ jsx15("span", { className: "text-[10px] text-slate-400 block truncate", children: "\u05E7\u05D5\u05D1\u05E5 \u05DE\u05D3\u05D9\u05D4 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05D1-Header" })
11881
+ /* @__PURE__ */ jsx15("span", { className: "text-[10px] text-slate-400 block truncate", children: hasValidMedia ? "\u05E7\u05D5\u05D1\u05E5 \u05EA\u05DE\u05D5\u05E0\u05D4 \u05DE\u05E7\u05D5\u05E9\u05E8 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA" : "\u05D8\u05E8\u05DD \u05D4\u05D5\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5 \u05EA\u05DE\u05D5\u05E0\u05D4" })
11798
11882
  ] })
11799
11883
  ] }) : headerFormat === "DOCUMENT" ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
11800
- /* @__PURE__ */ jsx15("div", { className: "w-8 h-8 rounded-lg bg-rose-50 text-rose-600 border border-rose-100 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx15(FileText3, { className: "w-4 h-4" }) }),
11884
+ /* @__PURE__ */ jsx15("div", { className: `w-8 h-8 rounded-lg flex items-center justify-center shrink-0 border ${hasValidMedia ? "bg-rose-50 text-rose-600 border-rose-100" : "bg-slate-100 text-slate-400 border-slate-200"}`, children: /* @__PURE__ */ jsx15(FileText3, { className: "w-4 h-4" }) }),
11801
11885
  /* @__PURE__ */ jsxs14("div", { className: "overflow-hidden", children: [
11802
11886
  /* @__PURE__ */ jsx15("span", { className: "text-xs font-bold text-slate-800 block truncate", children: "\u05DE\u05E1\u05DE\u05DA \u05DB\u05D5\u05EA\u05E8\u05EA (PDF)" }),
11803
- /* @__PURE__ */ jsx15("span", { className: "text-[10px] text-slate-500 font-mono block truncate", title: docFilename, children: docFilename })
11887
+ /* @__PURE__ */ jsx15("span", { className: "text-[10px] text-slate-500 font-mono block truncate", title: hasValidMedia ? docFilename : void 0, children: hasValidMedia ? docFilename : "\u05D8\u05E8\u05DD \u05D4\u05D5\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5 \u05DE\u05E1\u05DE\u05DA" })
11804
11888
  ] })
11805
11889
  ] }) : /* @__PURE__ */ jsxs14(Fragment4, { children: [
11806
- /* @__PURE__ */ jsx15("div", { className: "w-8 h-8 rounded-lg bg-indigo-50 text-indigo-600 border border-indigo-100 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx15(Video2, { className: "w-4 h-4" }) }),
11890
+ /* @__PURE__ */ jsx15("div", { className: `w-8 h-8 rounded-lg flex items-center justify-center shrink-0 border ${hasValidMedia ? "bg-indigo-50 text-indigo-600 border-indigo-100" : "bg-slate-100 text-slate-400 border-slate-200"}`, children: /* @__PURE__ */ jsx15(Video2, { className: "w-4 h-4" }) }),
11807
11891
  /* @__PURE__ */ jsxs14("div", { className: "overflow-hidden", children: [
11808
11892
  /* @__PURE__ */ jsx15("span", { className: "text-xs font-bold text-slate-800 block truncate", children: "\u05E1\u05E8\u05D8\u05D5\u05DF \u05DB\u05D5\u05EA\u05E8\u05EA" }),
11809
- /* @__PURE__ */ jsx15("span", { className: "text-[10px] text-slate-400 block truncate", children: "\u05E7\u05D5\u05D1\u05E5 \u05D5\u05D9\u05D3\u05D0\u05D5 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05D1-Header" })
11893
+ /* @__PURE__ */ jsx15("span", { className: "text-[10px] text-slate-400 block truncate", children: hasValidMedia ? "\u05E7\u05D5\u05D1\u05E5 \u05D5\u05D9\u05D3\u05D0\u05D5 \u05DE\u05E7\u05D5\u05E9\u05E8 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA" : "\u05D8\u05E8\u05DD \u05D4\u05D5\u05E2\u05DC\u05D4 \u05E7\u05D5\u05D1\u05E5 \u05D5\u05D9\u05D3\u05D0\u05D5" })
11810
11894
  ] })
11811
11895
  ] }) }),
11812
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5 shrink-0", children: [
11813
- headerMediaUrl && headerMediaUrl.startsWith("http") && /* @__PURE__ */ jsx15(
11896
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 shrink-0", children: [
11897
+ hasValidMedia ? /* @__PURE__ */ jsxs14("span", { className: "inline-flex items-center gap-1 text-[10px] font-medium text-emerald-800 px-2 py-0.5 bg-emerald-50 border border-emerald-200 rounded-lg", children: [
11898
+ /* @__PURE__ */ jsx15(CheckCircle23, { className: "w-3 h-3 text-emerald-600" }),
11899
+ "\u05E7\u05D5\u05D1\u05E5 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05EA\u05E7\u05D9\u05DF"
11900
+ ] }) : /* @__PURE__ */ jsxs14("span", { className: "inline-flex items-center gap-1 text-[10px] font-semibold text-amber-800 px-2 py-0.5 bg-amber-50 border border-amber-200 rounded-lg", children: [
11901
+ /* @__PURE__ */ jsx15(AlertCircle3, { className: "w-3 h-3 text-amber-600 shrink-0" }),
11902
+ "\u05E0\u05D3\u05E8\u05E9 \u05E6\u05D9\u05E8\u05D5\u05E3 \u05E7\u05D5\u05D1\u05E5 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5"
11903
+ ] }),
11904
+ hasValidMedia && /* @__PURE__ */ jsx15(
11814
11905
  "a",
11815
11906
  {
11816
11907
  href: headerMediaUrl,
@@ -11821,7 +11912,44 @@ function TemplateSelectionBlock({
11821
11912
  children: /* @__PURE__ */ jsx15(ExternalLink4, { className: "w-3.5 h-3.5" })
11822
11913
  }
11823
11914
  ),
11824
- /* @__PURE__ */ jsx15("span", { className: "text-[9px] text-emerald-800 font-semibold px-1.5 py-0.5 bg-emerald-50 border border-emerald-200 rounded-md", children: "\u05DE\u05E6\u05D5\u05E8\u05E3 \u05D0\u05D5\u05D8\u05D5\u05DE\u05D8\u05D9\u05EA" })
11915
+ /* @__PURE__ */ jsx15(
11916
+ "input",
11917
+ {
11918
+ ref: fileInputRef,
11919
+ type: "file",
11920
+ className: "hidden",
11921
+ accept: headerFormat === "DOCUMENT" ? ".pdf,application/pdf" : headerFormat === "VIDEO" ? "video/mp4,video/*" : "image/png,image/jpeg,image/jpg",
11922
+ onChange: (e) => {
11923
+ var _a;
11924
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
11925
+ if (file && handleFileUpload && (selectedTemplate == null ? void 0 : selectedTemplate.name)) {
11926
+ handleFileUpload(file, selectedTemplate.name);
11927
+ }
11928
+ e.target.value = "";
11929
+ },
11930
+ disabled: isUploadingMedia
11931
+ }
11932
+ ),
11933
+ /* @__PURE__ */ jsx15(
11934
+ "button",
11935
+ {
11936
+ type: "button",
11937
+ disabled: isUploadingMedia,
11938
+ onClick: () => {
11939
+ var _a;
11940
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
11941
+ },
11942
+ className: `inline-flex items-center gap-1.5 px-2.5 py-1 text-[11px] font-medium rounded-lg transition-colors border shadow-2xs ${isUploadingMedia ? "bg-slate-100 text-slate-400 border-slate-200 cursor-not-allowed" : hasValidMedia ? "bg-white text-slate-700 hover:bg-slate-50 border-slate-200 hover:border-slate-300" : "bg-emerald-600 text-white hover:bg-emerald-700 border-emerald-600"}`,
11943
+ title: hasValidMedia ? "\u05D4\u05D7\u05DC\u05E3 \u05E7\u05D5\u05D1\u05E5 \u05DE\u05D3\u05D9\u05D4 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5" : `${uploadButtonLabel} \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5`,
11944
+ children: isUploadingMedia ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
11945
+ /* @__PURE__ */ jsx15(Loader25, { className: "w-3.5 h-3.5 animate-spin" }),
11946
+ /* @__PURE__ */ jsx15("span", { children: "\u05DE\u05E2\u05DC\u05D4..." })
11947
+ ] }) : /* @__PURE__ */ jsxs14(Fragment4, { children: [
11948
+ /* @__PURE__ */ jsx15(Upload3, { className: "w-3.5 h-3.5" }),
11949
+ /* @__PURE__ */ jsx15("span", { children: hasValidMedia ? "\u05D4\u05D7\u05DC\u05E3 \u05E7\u05D5\u05D1\u05E5" : uploadButtonLabel })
11950
+ ] })
11951
+ }
11952
+ )
11825
11953
  ] })
11826
11954
  ] });
11827
11955
  })(),
@@ -12031,7 +12159,7 @@ import {
12031
12159
  Users,
12032
12160
  ChevronDown as ChevronDown5,
12033
12161
  Loader2 as Loader26,
12034
- AlertCircle as AlertCircle3,
12162
+ AlertCircle as AlertCircle4,
12035
12163
  Send,
12036
12164
  X as X11,
12037
12165
  CheckSquare as CheckSquare2
@@ -12090,7 +12218,7 @@ function SettingsAndActionsBlock({
12090
12218
  /* @__PURE__ */ jsx17(Loader26, { className: "w-4 h-4 animate-spin text-emerald-600 shrink-0" }),
12091
12219
  /* @__PURE__ */ jsx17("span", { className: "font-medium text-[11px] text-slate-600", children: "\u05E1\u05D5\u05E8\u05E7 \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4 \u05D5\u05D1\u05D8\u05D1\u05DC\u05D0\u05D5\u05EA \u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA..." })
12092
12220
  ] }) : targetPhoneOptions.length === 0 ? /* @__PURE__ */ jsxs16("div", { className: "p-2.5 bg-amber-50 border border-amber-200 rounded-xl text-amber-900 text-xs flex items-center gap-2 shadow-2xs", children: [
12093
- /* @__PURE__ */ jsx17(AlertCircle3, { className: "w-4 h-4 text-amber-600 shrink-0" }),
12221
+ /* @__PURE__ */ jsx17(AlertCircle4, { className: "w-4 h-4 text-amber-600 shrink-0" }),
12094
12222
  /* @__PURE__ */ jsx17("span", { className: "font-semibold text-[11px]", children: "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D4 \u05E2\u05DE\u05D5\u05D3\u05EA \u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05D8\u05D1\u05DC\u05D4" })
12095
12223
  ] }) : targetPhoneOptions.length === 1 && targetPhoneOptions[0].type === "DIRECT" ? /* @__PURE__ */ jsxs16("div", { className: "space-y-1", children: [
12096
12224
  /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between", children: [
@@ -12534,7 +12662,7 @@ function WhatsAppComponent(props) {
12534
12662
  className: `px-6 py-3 border-b text-xs flex items-center justify-between gap-3 ${state.feedback.type === "success" ? "bg-emerald-50 border-emerald-200 text-emerald-800" : "bg-rose-50 border-rose-200 text-rose-800"}`,
12535
12663
  children: [
12536
12664
  /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
12537
- state.feedback.type === "success" ? /* @__PURE__ */ jsx18(CheckCircle23, { className: "w-4 h-4 text-emerald-600 shrink-0" }) : /* @__PURE__ */ jsx18(AlertCircle4, { className: "w-4 h-4 text-rose-600 shrink-0" }),
12665
+ state.feedback.type === "success" ? /* @__PURE__ */ jsx18(CheckCircle24, { className: "w-4 h-4 text-emerald-600 shrink-0" }) : /* @__PURE__ */ jsx18(AlertCircle5, { className: "w-4 h-4 text-rose-600 shrink-0" }),
12538
12666
  /* @__PURE__ */ jsx18("span", { children: state.feedback.message })
12539
12667
  ] }),
12540
12668
  /* @__PURE__ */ jsx18(
@@ -12551,7 +12679,7 @@ function WhatsAppComponent(props) {
12551
12679
  ),
12552
12680
  state.fetchError && !state.isLoadingTemplates && /* @__PURE__ */ jsxs17("div", { className: "px-6 py-3 bg-amber-50 border-b border-amber-200 text-xs text-amber-900 flex items-center justify-between", children: [
12553
12681
  /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
12554
- /* @__PURE__ */ jsx18(AlertCircle4, { className: "w-4 h-4 text-amber-600 shrink-0" }),
12682
+ /* @__PURE__ */ jsx18(AlertCircle5, { className: "w-4 h-4 text-amber-600 shrink-0" }),
12555
12683
  /* @__PURE__ */ jsxs17("span", { children: [
12556
12684
  "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D8\u05E2\u05D9\u05E0\u05EA \u05EA\u05D1\u05E0\u05D9\u05D5\u05EA \u05DE-Meta: ",
12557
12685
  state.fetchError
@@ -12592,7 +12720,9 @@ function WhatsAppComponent(props) {
12592
12720
  handleDeleteTemplate: state.handleDeleteTemplate,
12593
12721
  isDeleting: state.isDeleting,
12594
12722
  setIsModalOpen: state.setIsModalOpen,
12595
- setFeedback: state.setFeedback
12723
+ setFeedback: state.setFeedback,
12724
+ isUploadingMedia: state.isUploadingMedia,
12725
+ handleFileUpload: state.handleFileUpload
12596
12726
  }
12597
12727
  ),
12598
12728
  /* @__PURE__ */ jsx18(
@@ -12662,7 +12792,7 @@ function WhatsAppComponent(props) {
12662
12792
  /* @__PURE__ */ jsx18(Loader27, { className: "w-6 h-6 animate-spin text-emerald-600" }),
12663
12793
  /* @__PURE__ */ jsx18("span", { children: "\u05D8\u05D5\u05E2\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DE\u05D4\u05D8\u05D1\u05DC\u05D4..." })
12664
12794
  ] }) : state.tableError ? /* @__PURE__ */ jsxs17("div", { className: "flex-1 flex items-center justify-center p-6 text-center text-xs text-rose-600 bg-rose-50 gap-2", children: [
12665
- /* @__PURE__ */ jsx18(AlertCircle4, { className: "w-4 h-4 text-rose-500 shrink-0" }),
12795
+ /* @__PURE__ */ jsx18(AlertCircle5, { className: "w-4 h-4 text-rose-500 shrink-0" }),
12666
12796
  /* @__PURE__ */ jsx18("span", { children: state.tableError })
12667
12797
  ] }) : !(settings == null ? void 0 : settings.tableMetaId) ? /* @__PURE__ */ jsx18("div", { className: "flex-1 flex items-center justify-center p-10 text-center text-xs text-slate-400", children: "\u05DC\u05D0 \u05E0\u05D1\u05D7\u05E8\u05D4 \u05D8\u05D1\u05DC\u05EA \u05DE\u05E7\u05D5\u05E8 \u05E2\u05D1\u05D5\u05E8 \u05E8\u05DB\u05D9\u05D1 WhatsApp \u05D6\u05D4" }) : /* @__PURE__ */ jsx18(
12668
12798
  TableBlockClient,
@@ -12778,7 +12908,7 @@ function WhatsAppComponent(props) {
12778
12908
  /* @__PURE__ */ jsx18("div", { className: "w-7 h-7 rounded-md bg-indigo-50 text-indigo-600 border border-indigo-100 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx18(Video3, { className: "w-3.5 h-3.5" }) }),
12779
12909
  /* @__PURE__ */ jsx18("span", { className: "font-semibold text-slate-800 truncate", children: "\u05E1\u05E8\u05D8\u05D5\u05DF \u05DB\u05D5\u05EA\u05E8\u05EA" })
12780
12910
  ] }) }),
12781
- /* @__PURE__ */ jsx18("span", { className: "text-[10px] text-emerald-700 bg-emerald-50 border border-emerald-200 px-1.5 py-0.5 rounded font-medium shrink-0", children: "\u05DE\u05E6\u05D5\u05E8\u05E3 \u05D0\u05D5\u05D8\u05D5\u05DE\u05D8\u05D9\u05EA" })
12911
+ state.headerMediaUrl && state.headerMediaUrl.startsWith("http") ? /* @__PURE__ */ jsx18("span", { className: "text-[10px] text-emerald-700 bg-emerald-50 border border-emerald-200 px-1.5 py-0.5 rounded font-medium shrink-0", children: "\u05E7\u05D5\u05D1\u05E5 \u05DE\u05E6\u05D5\u05E8\u05E3 \u05EA\u05E7\u05D9\u05DF" }) : /* @__PURE__ */ jsx18("span", { className: "text-[10px] text-amber-700 bg-amber-50 border border-amber-200 px-1.5 py-0.5 rounded font-medium shrink-0", children: "\u05E0\u05D3\u05E8\u05E9 \u05E6\u05D9\u05E8\u05D5\u05E3 \u05E7\u05D5\u05D1\u05E5 \u05DC\u05EA\u05D1\u05E0\u05D9\u05EA \u05D6\u05D5" })
12782
12912
  ] });
12783
12913
  })(),
12784
12914
  /* @__PURE__ */ jsxs17("div", { className: "space-y-1.5", children: [
@@ -12814,7 +12944,7 @@ function WhatsAppComponent(props) {
12814
12944
  /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-slate-500 leading-normal", children: "\u05D4\u05D5\u05D3\u05E2\u05EA \u05D4\u05D1\u05D3\u05D9\u05E7\u05D4 \u05EA\u05D9\u05E9\u05DC\u05D7 \u05D9\u05E9\u05D9\u05E8\u05D5\u05EA \u05DC\u05DE\u05E1\u05E4\u05E8 \u05D6\u05D4 \u05DC\u05E6\u05D5\u05E8\u05DA \u05D5\u05D9\u05D3\u05D5\u05D0 \u05EA\u05E7\u05D9\u05E0\u05D5\u05EA \u05EA\u05D5\u05DB\u05DF \u05D4\u05D4\u05D5\u05D3\u05E2\u05D4 \u05D5\u05D4\u05E2\u05D9\u05E6\u05D5\u05D1." })
12815
12945
  ] }),
12816
12946
  state.testFeedback && /* @__PURE__ */ jsxs17("div", { className: "p-2.5 bg-rose-50 border border-rose-200 rounded-xl text-xs text-rose-700 flex items-center gap-2", children: [
12817
- /* @__PURE__ */ jsx18(AlertCircle4, { className: "w-4 h-4 text-rose-500 shrink-0" }),
12947
+ /* @__PURE__ */ jsx18(AlertCircle5, { className: "w-4 h-4 text-rose-500 shrink-0" }),
12818
12948
  /* @__PURE__ */ jsx18("span", { children: state.testFeedback })
12819
12949
  ] })
12820
12950
  ] }),
@@ -12837,9 +12967,10 @@ function WhatsAppComponent(props) {
12837
12967
  "button",
12838
12968
  {
12839
12969
  type: "button",
12840
- disabled: !state.testPhoneNumber.trim() || state.isSendingTest,
12970
+ disabled: !state.testPhoneNumber.trim() || state.isSendingTest || state.isTestDisabled,
12841
12971
  onClick: state.handleSendTest,
12842
- className: `px-4 py-2 text-xs font-bold rounded-xl transition-all flex items-center gap-1.5 ${!state.testPhoneNumber.trim() || state.isSendingTest ? "bg-emerald-600/40 text-white/70 cursor-not-allowed" : "bg-emerald-600 hover:bg-emerald-700 active:scale-[0.98] text-white shadow-xs cursor-pointer"}`,
12972
+ title: state.testButtonTitle,
12973
+ className: `px-4 py-2 text-xs font-bold rounded-xl transition-all flex items-center gap-1.5 ${!state.testPhoneNumber.trim() || state.isSendingTest || state.isTestDisabled ? "bg-emerald-600/40 text-white/70 cursor-not-allowed" : "bg-emerald-600 hover:bg-emerald-700 active:scale-[0.98] text-white shadow-xs cursor-pointer"}`,
12843
12974
  children: state.isSendingTest ? /* @__PURE__ */ jsxs17(Fragment7, { children: [
12844
12975
  /* @__PURE__ */ jsx18(Loader27, { className: "w-3.5 h-3.5 animate-spin" }),
12845
12976
  /* @__PURE__ */ jsx18("span", { children: "\u05E9\u05D5\u05DC\u05D7..." })
package/dist/phone.d.mts CHANGED
@@ -35,10 +35,14 @@ declare function isValidPhoneNumber(phone: string): boolean;
35
35
  */
36
36
  declare function generateWhatsAppLink(phone: string, defaultCountryCode?: string): string | null;
37
37
  /**
38
- * Normalizes a phone number for WhatsApp sending (E.164 without leading '+').
39
- * - Strips spaces, dashes, and special characters.
40
- * - If it starts with '05' (10 digits), replaces the leading '0' with '972' (e.g., '0586812807' -> '972586812807').
41
- * - If it starts with '+', strips the '+'.
38
+ * Normalizes a phone number for WhatsApp sending (Meta international format without leading '+').
39
+ * - Returns empty string if input is falsy.
40
+ * - Trims input and removes leading '+' if it exists.
41
+ * - Strips all non-digit characters.
42
+ * - If starting with '972', returns it as is.
43
+ * - If starting with '0' (Israeli mobile/landline), strips '0' and prepends '972'.
44
+ * - If exactly 9 digits long (omitted leading zero), prepends '972'.
45
+ * - Returns the processed digits string.
42
46
  */
43
47
  declare function normalizePhoneForWhatsApp(phone: string): string;
44
48
 
package/dist/phone.d.ts CHANGED
@@ -35,10 +35,14 @@ declare function isValidPhoneNumber(phone: string): boolean;
35
35
  */
36
36
  declare function generateWhatsAppLink(phone: string, defaultCountryCode?: string): string | null;
37
37
  /**
38
- * Normalizes a phone number for WhatsApp sending (E.164 without leading '+').
39
- * - Strips spaces, dashes, and special characters.
40
- * - If it starts with '05' (10 digits), replaces the leading '0' with '972' (e.g., '0586812807' -> '972586812807').
41
- * - If it starts with '+', strips the '+'.
38
+ * Normalizes a phone number for WhatsApp sending (Meta international format without leading '+').
39
+ * - Returns empty string if input is falsy.
40
+ * - Trims input and removes leading '+' if it exists.
41
+ * - Strips all non-digit characters.
42
+ * - If starting with '972', returns it as is.
43
+ * - If starting with '0' (Israeli mobile/landline), strips '0' and prepends '972'.
44
+ * - If exactly 9 digits long (omitted leading zero), prepends '972'.
45
+ * - Returns the processed digits string.
42
46
  */
43
47
  declare function normalizePhoneForWhatsApp(phone: string): string;
44
48
 
package/dist/phone.js CHANGED
@@ -88,9 +88,15 @@ function normalizePhoneForWhatsApp(phone) {
88
88
  cleaned = cleaned.substring(1);
89
89
  }
90
90
  const digitsOnly = cleaned.replace(/\D/g, "");
91
- if (digitsOnly.startsWith("05") && digitsOnly.length === 10) {
91
+ if (digitsOnly.startsWith("972")) {
92
+ return digitsOnly;
93
+ }
94
+ if (digitsOnly.startsWith("0")) {
92
95
  return `972${digitsOnly.substring(1)}`;
93
96
  }
97
+ if (digitsOnly.length === 9) {
98
+ return `972${digitsOnly}`;
99
+ }
94
100
  return digitsOnly;
95
101
  }
96
102
  // Annotate the CommonJS export names for ESM import in node:
package/dist/phone.mjs CHANGED
@@ -57,9 +57,15 @@ function normalizePhoneForWhatsApp(phone) {
57
57
  cleaned = cleaned.substring(1);
58
58
  }
59
59
  const digitsOnly = cleaned.replace(/\D/g, "");
60
- if (digitsOnly.startsWith("05") && digitsOnly.length === 10) {
60
+ if (digitsOnly.startsWith("972")) {
61
+ return digitsOnly;
62
+ }
63
+ if (digitsOnly.startsWith("0")) {
61
64
  return `972${digitsOnly.substring(1)}`;
62
65
  }
66
+ if (digitsOnly.length === 9) {
67
+ return `972${digitsOnly}`;
68
+ }
63
69
  return digitsOnly;
64
70
  }
65
71
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@universe-platform/ui",
3
- "version": "0.1.0-beta.22",
3
+ "version": "0.1.0-beta.24",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",