The page you're looking for doesn't exist or has been moved.
- Don't worry, we're here to help.
+ If you think this is a mistake,{" "}
+
+ please file an issue.
+
+>;
+
+export function LanguageSelect({
+ name,
+ required,
+ value,
+ defaultValue,
+ onValueChange,
+ options = supported_form_page_languages,
+ optionsmap,
+ className,
+ placeholder = "Select language",
+}: {
+ name?: string;
+ required?: boolean;
+ value?: LanguageCode;
+ defaultValue?: LanguageCode;
+ onValueChange?: (value: LanguageCode) => void;
+ options?: LanguageCode[];
+ optionsmap?: LanguageSelectOptionMap;
+ className?: string;
+ placeholder?: string;
+}) {
+ return (
+
{
+ onValueChange?.(value as LanguageCode);
+ }}
+ >
+
+
+
+
+ {options.map((lang) => {
+ const disabled = optionsmap?.[lang]?.disabled;
+ return (
+
+ {language_label_map[lang].flag} {language_label_map[lang].label}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/apps/forms/components/sidebar/index.tsx b/apps/forms/components/sidebar/index.tsx
index 15292ce07..ba20adcb8 100644
--- a/apps/forms/components/sidebar/index.tsx
+++ b/apps/forms/components/sidebar/index.tsx
@@ -104,7 +104,10 @@ export function SidebarMenuLink({
return (
{/* override selected prop */}
- {React.cloneElement(children as any, { selected })}
+ {React.cloneElement(children as any, {
+ selected,
+ className: "cursor-pointer",
+ })}
);
}
@@ -149,7 +152,7 @@ export const SidebarMenuItem = React.forwardRef(function SidebarMenuItem(
"relative group",
"w-full px-2 py-1 rounded text-sm font-medium text-foreground",
"text-ellipsis whitespace-nowrap overflow-hidden",
- "hover:bg-accent hover:text-accent-foreground",
+ "hover:bg-accent hover:text-accent-foreground cursor-default",
"data-[muted='true']:text-muted-foreground",
"data-[disabled='true']:cursor-not-allowed data-[disabled='true']:opacity-40 data-[disabled='true']:bg-background",
"data-[selected='true']:bg-accent data-[selected='true']:text-accent-foreground",
diff --git a/apps/forms/database.types.ts b/apps/forms/database.types.ts
index 23142d2bc..18a0c549c 100644
--- a/apps/forms/database.types.ts
+++ b/apps/forms/database.types.ts
@@ -799,6 +799,7 @@ export type Database = {
is_scheduling_enabled: boolean
max_form_responses_by_customer: number | null
max_form_responses_in_total: number | null
+ name: string
project_id: number
scheduling_close_at: string | null
scheduling_open_at: string | null
@@ -819,6 +820,7 @@ export type Database = {
is_scheduling_enabled?: boolean
max_form_responses_by_customer?: number | null
max_form_responses_in_total?: number | null
+ name?: string
project_id: number
scheduling_close_at?: string | null
scheduling_open_at?: string | null
@@ -839,6 +841,7 @@ export type Database = {
is_scheduling_enabled?: boolean
max_form_responses_by_customer?: number | null
max_form_responses_in_total?: number | null
+ name?: string
project_id?: number
scheduling_close_at?: string | null
scheduling_open_at?: string | null
@@ -957,57 +960,67 @@ export type Database = {
}
form_document: {
Row: {
+ __name: string
background: Json | null
created_at: string
ending_page_i18n_overrides: Json | null
ending_page_template_id: string | null
form_id: string
+ g11n_manifest_id: number | null
id: string
is_ending_page_enabled: boolean
is_powered_by_branding_enabled: boolean
is_redirect_after_response_uri_enabled: boolean
- lang: Database["grida_forms"]["Enums"]["form_page_language"]
+ lang: Database["public"]["Enums"]["language_code"]
method: Database["grida_forms"]["Enums"]["form_method"]
- name: string
project_id: number
redirect_after_response_uri: string | null
stylesheet: Json | null
}
Insert: {
+ __name?: string
background?: Json | null
created_at?: string
ending_page_i18n_overrides?: Json | null
ending_page_template_id?: string | null
form_id: string
+ g11n_manifest_id?: number | null
id: string
is_ending_page_enabled?: boolean
is_powered_by_branding_enabled?: boolean
is_redirect_after_response_uri_enabled?: boolean
- lang?: Database["grida_forms"]["Enums"]["form_page_language"]
+ lang?: Database["public"]["Enums"]["language_code"]
method?: Database["grida_forms"]["Enums"]["form_method"]
- name?: string
project_id: number
redirect_after_response_uri?: string | null
stylesheet?: Json | null
}
Update: {
+ __name?: string
background?: Json | null
created_at?: string
ending_page_i18n_overrides?: Json | null
ending_page_template_id?: string | null
form_id?: string
+ g11n_manifest_id?: number | null
id?: string
is_ending_page_enabled?: boolean
is_powered_by_branding_enabled?: boolean
is_redirect_after_response_uri_enabled?: boolean
- lang?: Database["grida_forms"]["Enums"]["form_page_language"]
+ lang?: Database["public"]["Enums"]["language_code"]
method?: Database["grida_forms"]["Enums"]["form_method"]
- name?: string
project_id?: number
redirect_after_response_uri?: string | null
stylesheet?: Json | null
}
Relationships: [
+ {
+ foreignKeyName: "form_document_g11n_manifest_id_fkey"
+ columns: ["g11n_manifest_id"]
+ isOneToOne: false
+ referencedRelation: "manifest"
+ referencedColumns: ["id"]
+ },
{
foreignKeyName: "form_document_id_fkey"
columns: ["id"]
@@ -1767,20 +1780,6 @@ export type Database = {
| "video"
| "json"
form_method: "post" | "get" | "dialog"
- form_page_language:
- | "en"
- | "ko"
- | "es"
- | "de"
- | "ja"
- | "fr"
- | "pt"
- | "it"
- | "ru"
- | "zh"
- | "ar"
- | "hi"
- | "nl"
form_response_unknown_field_handling_strategy_type:
| "ignore"
| "accept"
@@ -1824,6 +1823,180 @@ export type Database = {
[_ in never]: never
}
}
+ grida_g11n: {
+ Tables: {
+ key: {
+ Row: {
+ created_at: string
+ description: string | null
+ id: number
+ keypath: string[]
+ manifest_id: number
+ updated_at: string
+ }
+ Insert: {
+ created_at?: string
+ description?: string | null
+ id?: number
+ keypath: string[]
+ manifest_id: number
+ updated_at: string
+ }
+ Update: {
+ created_at?: string
+ description?: string | null
+ id?: number
+ keypath?: string[]
+ manifest_id?: number
+ updated_at?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: "key_manifest_id_fkey"
+ columns: ["manifest_id"]
+ isOneToOne: false
+ referencedRelation: "manifest"
+ referencedColumns: ["id"]
+ },
+ ]
+ }
+ locale: {
+ Row: {
+ code: string
+ created_at: string
+ id: number
+ manifest_id: number
+ updated_at: string
+ }
+ Insert: {
+ code: string
+ created_at?: string
+ id?: number
+ manifest_id: number
+ updated_at: string
+ }
+ Update: {
+ code?: string
+ created_at?: string
+ id?: number
+ manifest_id?: number
+ updated_at?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: "locale_manifest_id_fkey"
+ columns: ["manifest_id"]
+ isOneToOne: false
+ referencedRelation: "manifest"
+ referencedColumns: ["id"]
+ },
+ ]
+ }
+ manifest: {
+ Row: {
+ created_at: string
+ default_locale_id: number | null
+ id: number
+ project_id: number
+ updated_at: string
+ }
+ Insert: {
+ created_at?: string
+ default_locale_id?: number | null
+ id?: number
+ project_id: number
+ updated_at?: string
+ }
+ Update: {
+ created_at?: string
+ default_locale_id?: number | null
+ id?: number
+ project_id?: number
+ updated_at?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: "manifest_default_locale_id_fkey"
+ columns: ["default_locale_id"]
+ isOneToOne: true
+ referencedRelation: "locale"
+ referencedColumns: ["id"]
+ },
+ {
+ foreignKeyName: "manifest_project_id_fkey"
+ columns: ["project_id"]
+ isOneToOne: false
+ referencedRelation: "project"
+ referencedColumns: ["id"]
+ },
+ ]
+ }
+ resource: {
+ Row: {
+ created_at: string
+ id: number
+ key_id: number
+ locale_id: number
+ manifest_id: number
+ updated_at: string
+ value: Json
+ }
+ Insert: {
+ created_at?: string
+ id?: number
+ key_id: number
+ locale_id: number
+ manifest_id: number
+ updated_at?: string
+ value: Json
+ }
+ Update: {
+ created_at?: string
+ id?: number
+ key_id?: number
+ locale_id?: number
+ manifest_id?: number
+ updated_at?: string
+ value?: Json
+ }
+ Relationships: [
+ {
+ foreignKeyName: "value_key_id_fkey"
+ columns: ["key_id"]
+ isOneToOne: false
+ referencedRelation: "key"
+ referencedColumns: ["id"]
+ },
+ {
+ foreignKeyName: "value_locale_id_fkey"
+ columns: ["locale_id"]
+ isOneToOne: false
+ referencedRelation: "locale"
+ referencedColumns: ["id"]
+ },
+ {
+ foreignKeyName: "value_manifest_id_fkey"
+ columns: ["manifest_id"]
+ isOneToOne: false
+ referencedRelation: "manifest"
+ referencedColumns: ["id"]
+ },
+ ]
+ }
+ }
+ Views: {
+ [_ in never]: never
+ }
+ Functions: {
+ [_ in never]: never
+ }
+ Enums: {
+ [_ in never]: never
+ }
+ CompositeTypes: {
+ [_ in never]: never
+ }
+ }
grida_sites: {
Tables: {
site_document: {
@@ -2399,6 +2572,12 @@ export type Database = {
}
Returns: number[]
}
+ rls_manifest: {
+ Args: {
+ p_manifest_id: number
+ }
+ Returns: boolean
+ }
rls_organization: {
Args: {
p_organization_id: number
@@ -2432,6 +2611,20 @@ export type Database = {
}
Enums: {
doctype: "v0_form" | "v0_site" | "v0_schema"
+ language_code:
+ | "en"
+ | "ko"
+ | "es"
+ | "de"
+ | "ja"
+ | "fr"
+ | "pt"
+ | "it"
+ | "ru"
+ | "zh"
+ | "ar"
+ | "hi"
+ | "nl"
}
CompositeTypes: {
[_ in never]: never
diff --git a/apps/forms/i18n/resources.common.ts b/apps/forms/i18n/resources.common.ts
new file mode 100644
index 000000000..125a407ed
--- /dev/null
+++ b/apps/forms/i18n/resources.common.ts
@@ -0,0 +1,99 @@
+///
+/// use for .min usage.
+///
+
+const common = {
+ en: {
+ next: "Next",
+ back: "Previous",
+ submit: "Submit",
+ pay: "Pay",
+ home: "Home",
+ },
+ es: {
+ next: "Siguiente",
+ back: "Anterior",
+ submit: "Enviar",
+ pay: "Pagar",
+ home: "Inicio",
+ },
+ ko: {
+ next: "다음",
+ back: "이전",
+ submit: "제출",
+ pay: "결제",
+ home: "홈",
+ },
+ ja: {
+ next: "次へ",
+ back: "戻る",
+ submit: "提出する",
+ pay: "支払う",
+ home: "ホーム",
+ },
+ zh: {
+ next: "下一步",
+ back: "上一步",
+ submit: "提交",
+ pay: "支付",
+ home: "首页",
+ },
+ fr: {
+ next: "Suivant",
+ back: "Précédent",
+ submit: "Soumettre",
+ pay: "Payer",
+ home: "Accueil",
+ },
+ pt: {
+ next: "Próximo",
+ back: "Anterior",
+ submit: "Enviar",
+ pay: "Pagar",
+ home: "Início",
+ },
+ it: {
+ next: "Avanti",
+ back: "Indietro",
+ submit: "Invia",
+ pay: "Paga",
+ home: "Home",
+ },
+ de: {
+ next: "Weiter",
+ back: "Zurück",
+ submit: "Einreichen",
+ pay: "Bezahlen",
+ home: "Startseite",
+ },
+ ru: {
+ next: "Далее",
+ back: "Назад",
+ submit: "Отправить",
+ pay: "Оплатить",
+ home: "Главная",
+ },
+ ar: {
+ next: "التالي",
+ back: "السابق",
+ submit: "إرسال",
+ pay: "دفع",
+ home: "الرئيسية",
+ },
+ hi: {
+ next: "अगला",
+ back: "पिछला",
+ submit: "जमा करें",
+ pay: "भुगतान करें",
+ home: "होम",
+ },
+ nl: {
+ next: "Volgende",
+ back: "Vorige",
+ submit: "Indienen",
+ pay: "Betalen",
+ home: "Home",
+ },
+};
+
+export default common;
diff --git a/apps/forms/i18n/resources.ts b/apps/forms/i18n/resources.ts
index 22bb46f43..eb7ecce53 100644
--- a/apps/forms/i18n/resources.ts
+++ b/apps/forms/i18n/resources.ts
@@ -1,6 +1,7 @@
import { TemplateVariables } from "@/lib/templating";
import type { ObjectPath } from "@/lib/templating/@types";
-import type { FormsPageLanguage } from "@/types";
+import type { LanguageCode } from "@/types";
+import common from "./resources.common";
type T = ObjectPath<
TemplateVariables.FormResponseContext & {
@@ -79,18 +80,14 @@ export interface Translation {
}
const resources: Record<
- FormsPageLanguage,
+ LanguageCode,
{
translation: Translation;
}
> = {
en: {
translation: {
- next: "Next",
- back: "Previous",
- submit: "Submit",
- pay: "Pay",
- home: "Home",
+ ...common.en,
left_in_stock: `${use("available")} left`,
sold_out: "Sold Out",
support_metadata: `Support Metadata`,
@@ -152,11 +149,7 @@ const resources: Record<
},
es: {
translation: {
- next: "Siguiente",
- back: "Anterior",
- submit: "Enviar",
- pay: "Pagar",
- home: "Inicio",
+ ...common.es,
left_in_stock: `${use("available")} restantes`,
sold_out: "Agotado",
support_metadata: `Metadatos de soporte`,
@@ -218,11 +211,7 @@ const resources: Record<
},
ko: {
translation: {
- next: "다음",
- back: "이전",
- submit: "제출",
- pay: "결제",
- home: "홈",
+ ...common.ko,
left_in_stock: `${use("available")}개 남음`,
sold_out: "매진됨",
support_metadata: `서포트 메타데이터`,
@@ -283,11 +272,7 @@ const resources: Record<
},
ja: {
translation: {
- next: "次へ",
- back: "戻る",
- submit: "提出する",
- pay: "支払う",
- home: "ホーム",
+ ...common.ja,
left_in_stock: `残り${use("available")}個`,
sold_out: "完売",
support_metadata: `サポートメタデータ`,
@@ -348,11 +333,7 @@ const resources: Record<
},
zh: {
translation: {
- next: "下一步",
- back: "上一步",
- submit: "提交",
- pay: "支付",
- home: "首页",
+ ...common.zh,
left_in_stock: `剩余${use("available")}件`,
sold_out: "售罄",
support_metadata: `支持元数据`,
@@ -410,11 +391,7 @@ const resources: Record<
},
fr: {
translation: {
- next: "Suivant",
- back: "Précédent",
- submit: "Soumettre",
- pay: "Payer",
- home: "Accueil",
+ ...common.fr,
left_in_stock: `${use("available")} restants`,
sold_out: "Épuisé",
support_metadata: `Métadonnées de support`,
@@ -476,11 +453,7 @@ const resources: Record<
},
pt: {
translation: {
- next: "Próximo",
- back: "Anterior",
- submit: "Enviar",
- pay: "Pagar",
- home: "Início",
+ ...common.pt,
left_in_stock: `${use("available")} restantes`,
sold_out: "Esgotado",
support_metadata: `Metadados de suporte`,
@@ -542,11 +515,7 @@ const resources: Record<
},
it: {
translation: {
- next: "Avanti",
- back: "Indietro",
- submit: "Invia",
- pay: "Paga",
- home: "Home",
+ ...common.it,
left_in_stock: `${use("available")} rimasti`,
sold_out: "Esaurito",
support_metadata: `Metadati di supporto`,
@@ -608,11 +577,7 @@ const resources: Record<
},
de: {
translation: {
- next: "Weiter",
- back: "Zurück",
- submit: "Einreichen",
- pay: "Bezahlen",
- home: "Startseite",
+ ...common.de,
left_in_stock: `${use("available")} übrig`,
sold_out: "Ausverkauft",
support_metadata: `Support-Metadaten`,
@@ -674,11 +639,7 @@ const resources: Record<
},
ru: {
translation: {
- next: "Далее",
- back: "Назад",
- submit: "Отправить",
- pay: "Оплатить",
- home: "Главная",
+ ...common.ru,
left_in_stock: `Осталось ${use("available")} шт.`,
sold_out: "Распродано",
support_metadata: `Метаданные поддержки`,
@@ -738,11 +699,7 @@ const resources: Record<
},
ar: {
translation: {
- next: "التالي",
- back: "السابق",
- submit: "إرسال",
- pay: "دفع",
- home: "الرئيسية",
+ ...common.ar,
left_in_stock: `${use("available")} متبقية`,
sold_out: "نفذت الكمية",
support_metadata: `بيانات الدعم الوصفية`,
@@ -802,11 +759,7 @@ const resources: Record<
},
hi: {
translation: {
- next: "अगला",
- back: "पिछला",
- submit: "जमा करें",
- pay: "भुगतान करें",
- home: "होम",
+ ...common.hi,
left_in_stock: `${use("available")} बचे हैं`,
sold_out: "बिक गया",
support_metadata: `सहायता मेटाडाटा`,
@@ -867,11 +820,7 @@ const resources: Record<
},
nl: {
translation: {
- next: "Volgende",
- back: "Vorige",
- submit: "Indienen",
- pay: "Betalen",
- home: "Home",
+ ...common.nl,
left_in_stock: `Nog ${use("available")} beschikbaar`,
sold_out: "Uitverkocht",
support_metadata: `Ondersteuningsmetadata`,
diff --git a/apps/forms/k/supported_field_types.ts b/apps/forms/k/supported_field_types.ts
index b6784f984..bb92c4ac4 100644
--- a/apps/forms/k/supported_field_types.ts
+++ b/apps/forms/k/supported_field_types.ts
@@ -206,6 +206,7 @@ const html5_checkbox_alias_field_types: FormInputType[] = [
const html5_placeholder_not_supported_field_types: FormInputType[] = [
...html5_file_alias_field_types,
...html5_checkbox_alias_field_types,
+ "hidden",
"toggle",
"toggle-group",
"radio",
diff --git a/apps/forms/k/supported_languages.ts b/apps/forms/k/supported_languages.ts
index d5a261c46..37b667aa8 100644
--- a/apps/forms/k/supported_languages.ts
+++ b/apps/forms/k/supported_languages.ts
@@ -1,22 +1,25 @@
-import { FormsPageLanguage } from "@/types";
+import { LanguageCode } from "@/types";
import resources from "@/i18n";
-export const supported_form_page_languages: FormsPageLanguage[] = Object.keys(
+export const supported_form_page_languages: LanguageCode[] = Object.keys(
resources
-) as FormsPageLanguage[];
+) as LanguageCode[];
-export const language_label_map: Record
= {
- en: "English",
- es: "Spanish / Español",
- de: "German / Deutsch",
- ja: "Japanese / 日本語",
- fr: "French / Français",
- pt: "Portuguese / Português",
- it: "Italian / Italiano",
- ko: "Korean / 한국어",
- ru: "Russian / Русский",
- zh: "Chinese / 中文",
- ar: "Arabic / العربية",
- hi: "Hindi / हिन्दी",
- nl: "Dutch / Nederlands",
+export const language_label_map: Record<
+ LanguageCode,
+ { flag: string; label: string }
+> = {
+ en: { flag: "🇺🇸", label: "English" },
+ es: { flag: "🇪🇸", label: "Spanish / Español" },
+ de: { flag: "🇩🇪", label: "German / Deutsch" },
+ ja: { flag: "🇯🇵", label: "Japanese / 日本語" },
+ fr: { flag: "🇫🇷", label: "French / Français" },
+ pt: { flag: "🇵🇹", label: "Portuguese / Português" },
+ it: { flag: "🇮🇹", label: "Italian / Italiano" },
+ ko: { flag: "🇰🇷", label: "Korean / 한국어" },
+ ru: { flag: "🇷🇺", label: "Russian / Русский" },
+ zh: { flag: "🇨🇳", label: "Chinese / 中文" },
+ ar: { flag: "🇸🇦", label: "Arabic / العربية" },
+ hi: { flag: "🇮🇳", label: "Hindi / हिन्दी" },
+ nl: { flag: "🇳🇱", label: "Dutch / Nederlands" },
};
diff --git a/apps/forms/k/video_block_defaults.ts b/apps/forms/k/video_block_defaults.ts
index efe54825d..7f3b234b0 100644
--- a/apps/forms/k/video_block_defaults.ts
+++ b/apps/forms/k/video_block_defaults.ts
@@ -1,2 +1,2 @@
export const VIDEO_BLOCK_SRC_DEFAULT_VALUE =
- "https://www.youtube.com/watch?v=BFhp7Y0iLSA&ab_channel=AbstractMotion";
+ "https://www.youtube.com/watch?v=jNQXAC9IVRw&ab_channel=jawed";
diff --git a/apps/forms/lib/forms/renderer.ts b/apps/forms/lib/forms/renderer.ts
index e989042e5..caf6f4cf4 100644
--- a/apps/forms/lib/forms/renderer.ts
+++ b/apps/forms/lib/forms/renderer.ts
@@ -5,7 +5,7 @@ import type {
FormFieldDefinition,
FormBlock,
Option,
- FormsPageLanguage,
+ LanguageCode,
} from "@/types";
import { blockstree } from "./tree";
import { FormBlockTree } from "./types";
@@ -187,7 +187,7 @@ export class FormRenderTree {
readonly id: string,
readonly title: string | null | undefined,
readonly description: string | null | undefined,
- readonly lang: FormsPageLanguage | null | undefined,
+ readonly lang: LanguageCode | null | undefined,
private readonly _m_fields: FormFieldDefinition[] = [],
private readonly _m_blocks?: FormBlock[] | null,
private readonly config?: RenderTreeConfig,
diff --git a/apps/forms/lib/forms/url.ts b/apps/forms/lib/forms/url.ts
index efffd907f..16184bd74 100644
--- a/apps/forms/lib/forms/url.ts
+++ b/apps/forms/lib/forms/url.ts
@@ -20,6 +20,7 @@ type EditorPageParamsMap = {
".": {};
form: {};
"form/edit": {};
+ "form/g11n": {};
settings: {};
design: {};
data: {};
@@ -58,6 +59,8 @@ export function editorlink(
return `${origin}/${basepath}/${id}/form`;
case "form/edit":
return `${origin}/${basepath}/${id}/form/edit`;
+ case "form/g11n":
+ return `${origin}/${basepath}/${id}/form/g11n`;
case "settings":
return `${origin}/${basepath}/${id}/settings`;
// case "settings/customize":
diff --git a/apps/forms/lib/formstate/core/agent.tsx b/apps/forms/lib/formstate/core/agent.tsx
index ce670b70c..c0f42e41d 100644
--- a/apps/forms/lib/formstate/core/agent.tsx
+++ b/apps/forms/lib/formstate/core/agent.tsx
@@ -9,8 +9,6 @@ export function FormAgentProvider({
}: React.PropsWithChildren<{ initial: FormAgentState }>) {
const [state, dispatch] = React.useReducer(reducer, initial);
- // console.log("FormAgentProvider", state.tree, initial.tree);
-
useEffect(() => {
dispatch({ type: "refresh", state: initial });
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/apps/forms/lib/simulator/index.ts b/apps/forms/lib/simulator/index.ts
index f127d135a..43d57732c 100644
--- a/apps/forms/lib/simulator/index.ts
+++ b/apps/forms/lib/simulator/index.ts
@@ -2,7 +2,7 @@ import { SYSTEM_X_GF_SIMULATOR_FLAG_KEY } from "@/k/system";
import { nanoid } from "nanoid";
import { v4 } from "uuid";
import { FormRenderTree } from "../forms";
-import { createClientFormsClient } from "../supabase/client";
+import { createClientComponentFormsClient } from "../supabase/client";
import assert from "assert";
import { FormDocument } from "@/types";
import { FormSubmitErrorCode } from "@/types/private/api";
@@ -62,7 +62,7 @@ export class Simulator {
}
private async _fetch_form_schema() {
- const { data } = await createClientFormsClient()
+ const { data } = await createClientComponentFormsClient()
.from("form")
.select(
`
diff --git a/apps/forms/lib/supabase/client.ts b/apps/forms/lib/supabase/client.ts
index 4924043ed..35258bf5e 100644
--- a/apps/forms/lib/supabase/client.ts
+++ b/apps/forms/lib/supabase/client.ts
@@ -1,7 +1,7 @@
import type { Database } from "@/database.types";
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
-export const createClientFormsClient = () =>
+export const createClientComponentFormsClient = () =>
createClientComponentClient({
options: {
db: {
@@ -10,7 +10,7 @@ export const createClientFormsClient = () =>
},
});
-export const createClientCommerceClient = () =>
+export const createClientComponentCommerceClient = () =>
createClientComponentClient({
options: {
db: {
@@ -20,7 +20,16 @@ export const createClientCommerceClient = () =>
isSingleton: false,
});
-export const createClientWorkspaceClient = () =>
+export const createClientComponentG11nClient = () =>
+ createClientComponentClient({
+ options: {
+ db: {
+ schema: "grida_g11n",
+ },
+ },
+ });
+
+export const createClientComponentWorkspaceClient = () =>
createClientComponentClient({
options: {
db: {
diff --git a/apps/forms/lib/supabase/server.ts b/apps/forms/lib/supabase/server.ts
index 29e0138d5..5fb9a744e 100644
--- a/apps/forms/lib/supabase/server.ts
+++ b/apps/forms/lib/supabase/server.ts
@@ -53,6 +53,17 @@ export const grida_xsupabase_client = createClient<
},
});
+export const grida_g11n_service_client = createClient(
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
+ process.env.SUPABASE_SERVICE_KEY!,
+ {
+ db: {
+ schema: "grida_g11n",
+ },
+ }
+);
+
+// TODO: rename to createServerComponentFormsClient
export const createServerComponentClient = (
cookieStore: ReadonlyRequestCookies
) =>
@@ -81,6 +92,21 @@ export const createServerComponentWorkspaceClient = (
}
);
+export const createServerComponentG11nClient = (
+ cookieStore: ReadonlyRequestCookies
+) =>
+ _createServerComponentClient(
+ {
+ cookies: () => cookieStore,
+ },
+ {
+ options: {
+ db: { schema: "grida_g11n" },
+ },
+ }
+ );
+
+// TODO: rename to createRouteHandlerFormsClient
export const createRouteHandlerClient = (cookieStore: ReadonlyRequestCookies) =>
_createRouteHandlerClient(
{
diff --git a/apps/forms/lib/supabase/types.ts b/apps/forms/lib/supabase/types.ts
new file mode 100644
index 000000000..4c380f8e2
--- /dev/null
+++ b/apps/forms/lib/supabase/types.ts
@@ -0,0 +1,18 @@
+import type { Database } from "@/database.types";
+import type { SupabaseClient } from "@supabase/supabase-js";
+
+export type TGridaCommerceSupabaseClient = SupabaseClient<
+ Database,
+ "grida_commerce"
+>;
+
+export type TGridaG11nSupabaseClient = SupabaseClient;
+
+export type TGridaFormsSupabaseClient = SupabaseClient;
+
+export type TGridaWorkspaceSupabaseClient = SupabaseClient;
+
+export type TGridaXSupabaseSupabaseClient = SupabaseClient<
+ Database,
+ "grida_x_supabase"
+>;
diff --git a/apps/forms/next.config.mjs b/apps/forms/next.config.mjs
index b7a85019b..6f8320029 100644
--- a/apps/forms/next.config.mjs
+++ b/apps/forms/next.config.mjs
@@ -33,6 +33,11 @@ const nextConfig = withMDX()({
destination: "/",
permanent: true,
},
+ {
+ source: "/issues/new",
+ destination: "https://github.com/gridaco/grida/issues/new/choose",
+ permanent: true,
+ },
// DO NOT ADD BELOW. this will match all paths with 3 segments.
// {
// source: "/:org/:proj/:id",
diff --git a/apps/forms/package.json b/apps/forms/package.json
index b8cce9bb5..4f664c24f 100644
--- a/apps/forms/package.json
+++ b/apps/forms/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint",
@@ -52,6 +52,7 @@
"@radix-ui/react-toolbar": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@react-email/components": "^0.0.18",
+ "@stepperize/react": "^3.0.1",
"@supabase/auth-helpers-nextjs": "^0.9.0",
"@supabase/postgrest-js": "^1.15.5",
"@supabase/ssr": "^0.1.0",
diff --git a/apps/forms/scaffolds/analytics/stats/index.tsx b/apps/forms/scaffolds/analytics/stats/index.tsx
index e8a7108f0..2ae9fe8fa 100644
--- a/apps/forms/scaffolds/analytics/stats/index.tsx
+++ b/apps/forms/scaffolds/analytics/stats/index.tsx
@@ -3,8 +3,8 @@
import type { Database } from "@/database.types";
import React, { useEffect, useMemo, useState } from "react";
import {
- createClientFormsClient,
- createClientWorkspaceClient,
+ createClientComponentFormsClient,
+ createClientComponentWorkspaceClient,
} from "@/lib/supabase/client";
import TimeSeriesChart from "../charts/timeseries";
import { GraphSkeleton, NumberSkeleton } from "../charts/skeleton";
@@ -183,7 +183,7 @@ export function Sessions({
from: Date;
to: Date;
}) {
- const supabase = useMemo(() => createClientFormsClient(), []);
+ const supabase = useMemo(() => createClientComponentFormsClient(), []);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
@@ -255,7 +255,7 @@ export function Customers({
from: Date;
to: Date;
}) {
- const supabase = useMemo(() => createClientWorkspaceClient(), []);
+ const supabase = useMemo(() => createClientComponentWorkspaceClient(), []);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
@@ -327,7 +327,7 @@ export function Responses({
from: Date;
to: Date;
}) {
- const supabase = useMemo(() => createClientFormsClient(), []);
+ const supabase = useMemo(() => createClientComponentFormsClient(), []);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
diff --git a/apps/forms/scaffolds/blocks-editor/blocks-editor.tsx b/apps/forms/scaffolds/blocks-editor/blocks-editor.tsx
index ad35d1434..78f94daef 100644
--- a/apps/forms/scaffolds/blocks-editor/blocks-editor.tsx
+++ b/apps/forms/scaffolds/blocks-editor/blocks-editor.tsx
@@ -1,6 +1,6 @@
"use client";
-import React, { useCallback, useEffect, useId, useRef } from "react";
+import React, { useCallback, useEffect, useId, useMemo, useRef } from "react";
import { type EditorFlatFormBlock, DRAFT_ID_START_WITH } from "../editor/state";
import { useEditorState } from "../editor";
import {
@@ -16,7 +16,7 @@ import {
SortableContext,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
-import { createClientFormsClient } from "@/lib/supabase/client";
+import { createClientComponentFormsClient } from "@/lib/supabase/client";
import toast from "react-hot-toast";
import { InsertMenuTrigger } from "./insert-menu-trigger";
import { SectionStyle } from "../agent/theme";
@@ -24,6 +24,10 @@ import { usePrevious } from "@uidotdev/usehooks";
import equal from "deep-equal";
import { FormPageBackgroundSchema, FormStyleSheetV1Schema } from "@/types";
import { FormAgentProvider, initdummy } from "@/lib/formstate";
+import { Button } from "@/components/ui/button";
+import { PoweredByGridaFooter } from "@/scaffolds/e/form/powered-by-brand-footer";
+import clsx from "clsx";
+import common from "@/i18n/resources.common";
export default function BlocksEditorRoot() {
return (
@@ -86,7 +90,7 @@ function DndContextProvider({ children }: React.PropsWithChildren<{}>) {
function PendingBlocksResolver() {
const [state, dispatch] = useEditorState();
- const supabase = createClientFormsClient();
+ const supabase = createClientComponentFormsClient();
const insertBlock = useCallback(
async (block: EditorFlatFormBlock) => {
@@ -150,7 +154,7 @@ function PendingBlocksResolver() {
function useSyncBlocks(blocks: EditorFlatFormBlock[]) {
// TODO: add debounce
- const supabase = createClientFormsClient();
+ const supabase = createClientComponentFormsClient();
const prevBlocksRef = useRef(blocks);
useEffect(() => {
@@ -221,11 +225,39 @@ function OptimisticBlocksSyncProvider({
return <>{children}>;
}
+function LangSyncProvider({ children }: React.PropsWithChildren<{}>) {
+ const [state] = useEditorState();
+ const { document_id } = state;
+ const prev = usePrevious(state.document.g11n);
+ const supabase = useMemo(() => createClientComponentFormsClient(), []);
+
+ useEffect(() => {
+ if (!prev) {
+ return;
+ }
+
+ // sync lang to server
+ if (!equal(prev, state.document.g11n)) {
+ // update lang
+ supabase
+ .from("form_document")
+ .update({
+ lang: state.document.g11n.lang,
+ })
+ .eq("id", document_id!)
+ .then(({ error }) => {
+ if (error) console.error(error);
+ });
+ }
+ }, [prev, document_id, state.document.g11n]);
+ return <>{children}>;
+}
+
function AgentThemeSyncProvider({ children }: React.PropsWithChildren<{}>) {
const [state] = useEditorState();
- const { document_id, theme } = state;
+ const { document_id, document, theme } = state;
const prev = usePrevious(state.theme);
- const supabase = createClientFormsClient();
+ const supabase = useMemo(() => createClientComponentFormsClient(), []);
useEffect(() => {
if (!prev) {
@@ -238,7 +270,6 @@ function AgentThemeSyncProvider({ children }: React.PropsWithChildren<{}>) {
supabase
.from("form_document")
.update({
- lang: theme.lang,
is_powered_by_branding_enabled: theme.is_powered_by_branding_enabled,
stylesheet: {
appearance: theme.appearance,
@@ -263,7 +294,6 @@ function AgentThemeSyncProvider({ children }: React.PropsWithChildren<{}>) {
supabase,
document_id,
theme.is_powered_by_branding_enabled,
- theme.lang,
theme.appearance,
theme.customCSS,
theme.fontFamily,
@@ -295,6 +325,7 @@ function BlocksEditor() {
+
b.id)}
@@ -311,11 +342,43 @@ function BlocksEditor() {
+