Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions app/actions/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,14 @@ func (action *ResendSignUpEmail) GetKind() enum.EmailVerificationKind {

// UpdateTenantSettings is the input model used to update tenant settings
type UpdateTenantSettings struct {
Logo *dto.ImageUpload `json:"logo"`
Title string `json:"title"`
Invitation string `json:"invitation"`
WelcomeMessage string `json:"welcomeMessage"`
WelcomeHeader string `json:"welcomeHeader"`
Locale string `json:"locale"`
CNAME string `json:"cname" format:"lower"`
Logo *dto.ImageUpload `json:"logo"`
Title string `json:"title"`
Invitation string `json:"invitation"`
WelcomeMessage string `json:"welcomeMessage"`
WelcomeHeader string `json:"welcomeHeader"`
DescriptionTemplate string `json:"descriptionTemplate"`
Locale string `json:"locale"`
CNAME string `json:"cname" format:"lower"`
}

func NewUpdateTenantSettings() *UpdateTenantSettings {
Expand Down Expand Up @@ -247,6 +248,10 @@ func (action *UpdateTenantSettings) Validate(ctx context.Context, user *entity.U
result.AddFieldFailure("welcomeHeader", "Welcome Header must have less than 100 characters.")
}

if len(action.DescriptionTemplate) > 2000 {
result.AddFieldFailure("descriptionTemplate", "Idea Template must have less than 2000 characters.")
}

if !i18n.IsValidLocale(action.Locale) {
result.AddFieldFailure("locale", "Locale is invalid.")
}
Expand Down
15 changes: 8 additions & 7 deletions app/handlers/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ func UpdateSettings() web.HandlerFunc {
Folder: "logos",
},
&cmd.UpdateTenantSettings{
Logo: action.Logo,
Title: action.Title,
Invitation: action.Invitation,
WelcomeMessage: action.WelcomeMessage,
WelcomeHeader: action.WelcomeHeader,
CNAME: action.CNAME,
Locale: action.Locale,
Logo: action.Logo,
Title: action.Title,
Invitation: action.Invitation,
WelcomeMessage: action.WelcomeMessage,
WelcomeHeader: action.WelcomeHeader,
DescriptionTemplate: action.DescriptionTemplate,
CNAME: action.CNAME,
Locale: action.Locale,
},
); err != nil {
return c.Failure(err)
Expand Down
15 changes: 8 additions & 7 deletions app/models/cmd/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ type UpdateTenantEmailAuthAllowedSettings struct {
}

type UpdateTenantSettings struct {
Logo *dto.ImageUpload
Title string
Invitation string
WelcomeMessage string
WelcomeHeader string
CNAME string
Locale string
Logo *dto.ImageUpload
Title string
Invitation string
WelcomeMessage string
WelcomeHeader string
DescriptionTemplate string
CNAME string
Locale string
}

type UpdateTenantAdvancedSettings struct {
Expand Down
1 change: 1 addition & 0 deletions app/models/entity/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Tenant struct {
Invitation string `json:"invitation"`
WelcomeMessage string `json:"welcomeMessage"`
WelcomeHeader string `json:"welcomeHeader"`
DescriptionTemplate string `json:"descriptionTemplate"`
CNAME string `json:"cname"`
Status enum.TenantStatus `json:"status"`
Locale string `json:"locale"`
Expand Down
2 changes: 2 additions & 0 deletions app/services/sqlstore/dbEntities/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Tenant struct {
Invitation string `db:"invitation"`
WelcomeMessage string `db:"welcome_message"`
WelcomeHeader string `db:"welcome_header"`
DescriptionTemplate string `db:"description_template"`
Status int `db:"status"`
Locale string `db:"locale"`
IsPrivate bool `db:"is_private"`
Expand Down Expand Up @@ -48,6 +49,7 @@ func (t *Tenant) ToModel() *entity.Tenant {
Invitation: t.Invitation,
WelcomeMessage: t.WelcomeMessage,
WelcomeHeader: t.WelcomeHeader,
DescriptionTemplate: t.DescriptionTemplate,
Status: enum.TenantStatus(t.Status),
Locale: t.Locale,
IsPrivate: t.IsPrivate,
Expand Down
9 changes: 5 additions & 4 deletions app/services/sqlstore/postgres/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ func updateTenantSettings(ctx context.Context, c *cmd.UpdateTenantSettings) erro
c.Logo.BlobKey = ""
}

query := "UPDATE tenants SET name = $1, invitation = $2, welcome_message = $3, welcome_header = $4, cname = $5, logo_bkey = $6, locale = $7 WHERE id = $8"
_, err := trx.Execute(query, c.Title, c.Invitation, c.WelcomeMessage, c.WelcomeHeader, c.CNAME, c.Logo.BlobKey, c.Locale, tenant.ID)
query := "UPDATE tenants SET name = $1, invitation = $2, welcome_message = $3, welcome_header = $4, description_template = $5, cname = $6, logo_bkey = $7, locale = $8 WHERE id = $9"
_, err := trx.Execute(query, c.Title, c.Invitation, c.WelcomeMessage, c.WelcomeHeader, c.DescriptionTemplate, c.CNAME, c.Logo.BlobKey, c.Locale, tenant.ID)
if err != nil {
return errors.Wrap(err, "failed update tenant settings")
}
Expand All @@ -91,6 +91,7 @@ func updateTenantSettings(ctx context.Context, c *cmd.UpdateTenantSettings) erro
tenant.CNAME = c.CNAME
tenant.WelcomeMessage = c.WelcomeMessage
tenant.WelcomeHeader = c.WelcomeHeader
tenant.DescriptionTemplate = c.DescriptionTemplate

return nil
})
Expand Down Expand Up @@ -259,7 +260,7 @@ func getFirstTenant(ctx context.Context, q *query.GetFirstTenant) error {
tenant := dbEntities.Tenant{}

err := trx.Get(&tenant, `
SELECT t.id, t.name, t.subdomain, t.cname, t.invitation, t.locale, t.welcome_message, t.welcome_header, t.status, t.is_private, t.logo_bkey, t.custom_css, t.allowed_schemes, t.is_email_auth_allowed, t.is_feed_enabled, t.is_moderation_enabled, t.prevent_indexing, t.is_pro,
SELECT t.id, t.name, t.subdomain, t.cname, t.invitation, t.locale, t.welcome_message, t.welcome_header, t.description_template, t.status, t.is_private, t.logo_bkey, t.custom_css, t.allowed_schemes, t.is_email_auth_allowed, t.is_feed_enabled, t.is_moderation_enabled, t.prevent_indexing, t.is_pro,
(b.paddle_subscription_id IS NOT NULL AND b.stripe_subscription_id IS NULL) AS has_paddle_subscription
FROM tenants t
LEFT JOIN tenants_billing b ON b.tenant_id = t.id
Expand All @@ -279,7 +280,7 @@ func getTenantByDomain(ctx context.Context, q *query.GetTenantByDomain) error {
tenant := dbEntities.Tenant{}

err := trx.Get(&tenant, `
SELECT t.id, t.name, t.subdomain, t.cname, t.invitation, t.locale, t.welcome_message, t.welcome_header, t.status, t.is_private, t.logo_bkey, t.custom_css, t.allowed_schemes, t.is_email_auth_allowed, t.is_feed_enabled, t.is_moderation_enabled, t.prevent_indexing, t.is_pro,
SELECT t.id, t.name, t.subdomain, t.cname, t.invitation, t.locale, t.welcome_message, t.welcome_header, t.description_template, t.status, t.is_private, t.logo_bkey, t.custom_css, t.allowed_schemes, t.is_email_auth_allowed, t.is_feed_enabled, t.is_moderation_enabled, t.prevent_indexing, t.is_pro,
(b.paddle_subscription_id IS NOT NULL AND b.stripe_subscription_id IS NULL) AS has_paddle_subscription
FROM tenants t
LEFT JOIN tenants_billing b ON b.tenant_id = t.id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE tenants ADD COLUMN description_template TEXT NOT NULL DEFAULT '';
1 change: 1 addition & 0 deletions public/models/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface Tenant {
invitation: string
welcomeMessage: string
welcomeHeader: string
descriptionTemplate: string
status: TenantStatus
isPrivate: boolean
logoBlobKey: string
Expand Down
16 changes: 15 additions & 1 deletion public/pages/Administration/pages/GeneralSettings.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ const GeneralSettingsPage = () => {
const [title, setTitle] = useState<string>(fider.session.tenant.name)
const [welcomeMessage, setWelcomeMessage] = useState<string>(fider.session.tenant.welcomeMessage)
const [welcomeHeader, setWelcomeHeader] = useState<string>(fider.session.tenant.welcomeHeader)
const [descriptionTemplate, setDescriptionTemplate] = useState<string>(fider.session.tenant.descriptionTemplate)
const [invitation, setInvitation] = useState<string>(fider.session.tenant.invitation)
const [logo, setLogo] = useState<ImageUpload | undefined>(undefined)
const [cname, setCNAME] = useState<string>(fider.session.tenant.cname)
const [locale, setLocale] = useState<string>(fider.session.tenant.locale)
const [error, setError] = useState<Failure | undefined>(undefined)

const handleSave = async (e: ButtonClickEvent) => {
const result = await actions.updateTenantSettings({ title, cname, welcomeMessage, welcomeHeader, invitation, logo, locale })
const result = await actions.updateTenantSettings({ title, cname, welcomeMessage, welcomeHeader, descriptionTemplate, invitation, logo, locale })
if (result.ok) {
e.preventEnable()
location.href = `/`
Expand Down Expand Up @@ -76,6 +77,19 @@ const GeneralSettingsPage = () => {
</p>
</TextArea>

<TextArea
field="descriptionTemplate"
label="Idea Template"
value={descriptionTemplate}
disabled={!fider.session.user.isAdministrator}
onChange={setDescriptionTemplate}
>
<p className="text-muted">
Prefilled into the description box when someone shares a new idea, so visitors fill in a structured template instead of a blank field. Markdown is
supported. Leave empty to use the default template.
</p>
</TextArea>

<Input
field="invitation"
label="Invitation"
Expand Down
9 changes: 7 additions & 2 deletions public/pages/Home/components/ShareFeedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,13 @@ export const ShareFeedback: React.FC<ShareFeedbackProps> = (props) => {
}

const canEditTags = fider.settings.postWithTags && props.tags.length > 0

const descriptionTemplate = fider.session.tenant.descriptionTemplate || ""
const hasCachedDraft = getCachedDescription() !== ""
const prefillTemplate = !hasCachedDraft && descriptionTemplate !== ""

const [title, setTitle] = useState(getCachedTitle())
const [description, setDescription] = useState(getCachedDescription())
const [description, setDescription] = useState(prefillTemplate ? descriptionTemplate : getCachedDescription())
const { attachments, handleImageUploaded, getImageSrc, clearAttachments } = useAttachments({
cacheKey: CACHE_KEYS.ATTACHMENT,
useLocalStorage: true,
Expand All @@ -66,7 +71,7 @@ export const ShareFeedback: React.FC<ShareFeedbackProps> = (props) => {
const [error, setError] = useState<Failure | undefined>(undefined)
const titleRef = useRef<HTMLInputElement>()
const editorRef = useRef<HTMLDivElement>(null)
const [titleManuallyEdited, setTitleManuallyEdited] = useState(getTitleManuallyEditedValue())
const [titleManuallyEdited, setTitleManuallyEdited] = useState(prefillTemplate ? true : getTitleManuallyEditedValue())
const [isInitialMount, setIsInitialMount] = useState(true)

useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions public/services/actions/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface UpdateTenantSettingsRequest {
invitation: string
welcomeMessage: string
welcomeHeader: string
descriptionTemplate: string
cname: string
locale: string
}
Expand Down