Skip to content
Merged
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
2 changes: 2 additions & 0 deletions addons/base/models/core_company.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ type CoreCompany struct {
MailChatterEnabled sdk.Boolean `sumeru:"string=Chatter,default=true"`
MailActivityPanelEnabled sdk.Boolean `sumeru:"string=Activity panel,default=true"`
Color sdk.Integer `sumeru:"string=Color Index"`
LoginLogo sdk.Text `sumeru:"string=Login logo"`
LoginTagline sdk.String `sumeru:"string=Login tagline"`
}
9 changes: 9 additions & 0 deletions addons/base/views/core_company_form_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
</group>
</group>

<group string="Login branding">
<group>
<field name="login_logo" string="Login logo" widget="image"/>
</group>
<group>
<field name="login_tagline" string="Login tagline" placeholder="Shown on the sign-in page…"/>
</group>
</group>

<group string="Address">
<group>
<field name="street" string="Street"/>
Expand Down
127 changes: 98 additions & 29 deletions core/engine/assets/css/sumeru-login.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,74 +15,143 @@
}

.sum-login-brand {
--sum-login-accent: var(--sum-header);
position: relative;
flex: 1 1 42%;
background: linear-gradient(145deg, var(--sum-header) 0%, var(--sum-header-dark) 55%, #1a2332 100%);
overflow: hidden;
background: linear-gradient(
155deg,
var(--sum-header-dark) 0%,
var(--sum-header) 42%,
color-mix(in srgb, var(--sum-login-accent) 35%, #1a2332) 100%
);
color: var(--sum-surface);
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2.5rem;
}

.sum-login-brand--accent-0 { --sum-login-accent: #875a7b; }
.sum-login-brand--accent-1 { --sum-login-accent: #9c9c9c; }
.sum-login-brand--accent-2 { --sum-login-accent: #67917a; }
.sum-login-brand--accent-3 { --sum-login-accent: #a06666; }
.sum-login-brand--accent-4 { --sum-login-accent: #8899aa; }
.sum-login-brand--accent-5 { --sum-login-accent: #d4a017; }
.sum-login-brand--accent-6 { --sum-login-accent: #6a5acd; }
.sum-login-brand--accent-7 { --sum-login-accent: #4682b4; }
.sum-login-brand--accent-8 { --sum-login-accent: #c0392b; }
.sum-login-brand--accent-9 { --sum-login-accent: #e67e22; }
.sum-login-brand--accent-10 { --sum-login-accent: #27ae60; }
.sum-login-brand--accent-11 { --sum-login-accent: #3498db; }

.sum-login-brand-bg {
position: absolute;
inset: 0;
pointer-events: none;
background-image:
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
background-size: 32px 32px;
mask-image: radial-gradient(ellipse 85% 70% at 30% 40%, #000 20%, transparent 75%);
}

.sum-login-brand-bg::after {
content: "";
position: absolute;
width: 420px;
height: 420px;
right: -120px;
top: -80px;
border-radius: 50%;
background: radial-gradient(circle, color-mix(in srgb, var(--sum-login-accent) 45%, transparent) 0%, transparent 70%);
opacity: 0.55;
}

.sum-login-brand-inner {
max-width: 420px;
position: relative;
z-index: 1;
max-width: 440px;
}

.sum-login-brand-logo {
.sum-login-brand-lockup {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.65rem;
margin: 0 0 1.75rem;
width: 72px;
height: 72px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.14);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.22);
}

.sum-login-brand-logo {
display: block;
width: 96px;
height: 96px;
object-fit: contain;
border-radius: 14px;
}

.sum-login-brand-kicker {
margin: 0 0 0.5rem;
font-size: var(--sum-font-size-sm);
font-weight: var(--sum-font-weight-semibold);
letter-spacing: 0.08em;
letter-spacing: 0.12em;
text-transform: uppercase;
opacity: 0.85;
opacity: 0.88;
color: color-mix(in srgb, var(--sum-login-accent) 55%, #fff);
}

.sum-login-brand-title {
margin: 0 0 1rem;
font-size: 1.75rem;
font-size: clamp(1.5rem, 2.5vw, 2rem);
font-weight: var(--sum-font-weight-bold);
line-height: 1.2;
letter-spacing: -0.02em;
line-height: 1.15;
letter-spacing: -0.025em;
}

.sum-login-brand-copy {
margin: 0 0 1.5rem;
font-size: var(--sum-font-size-md);
line-height: 1.55;
opacity: 0.92;
opacity: 0.9;
max-width: 36ch;
}

.sum-login-trust-list {
.sum-login-trust-chips {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 0;
padding: 0;
list-style: none;
}

.sum-login-trust-chip {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.35rem 0.65rem;
font-size: var(--sum-font-size-sm);
line-height: 1.6;
opacity: 0.9;
line-height: 1.35;
border-radius: 999px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.12);
}

.sum-login-trust-list li {
position: relative;
padding-left: 1.25rem;
margin-bottom: 0.35rem;
.sum-login-trust-chip svg {
flex-shrink: 0;
opacity: 0.9;
}

.sum-login-trust-list li::before {
content: "";
position: absolute;
left: 0;
top: 0.55em;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--sum-surface);
opacity: 0.75;
.sum-login-powered {
margin: 2rem 0 0;
font-size: var(--sum-font-size-sm);
letter-spacing: 0.04em;
text-transform: uppercase;
opacity: 0.65;
}

.sum-login-panel {
Expand Down Expand Up @@ -229,7 +298,7 @@
font-size: 1.35rem;
}

.sum-login-trust-list {
.sum-login-trust-chips {
display: none;
}
}
Expand Down
10 changes: 10 additions & 0 deletions core/engine/assets/img/sumeru-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 3 additions & 5 deletions core/engine/render/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ func RenderPage(ctx context.Context, templatesDir string, data PageData, bootstr
if len(data.SWCBootstrapJSON) == 0 {
data.SWCBootstrapJSON = BuildSWCBootstrapJSON(ctx, data, bootstrapWorkspace)
}
tmpl, err := template.ParseFiles(
filepath.Join(templatesDir, "base.html"),
filepath.Join(templatesDir, "shell_partials.html"),
)
paths := ShellLayoutTemplateFiles(templatesDir)
tmpl, err := template.ParseFiles(paths...)
if err != nil {
return "", err
}
var buf bytes.Buffer
// Must execute the layout by name: the partials file only defines {{template "sumMenuIcon"}}.
if err := tmpl.ExecuteTemplate(&buf, "base.html", data); err != nil {
if err := tmpl.ExecuteTemplate(&buf, filepath.Base(TemplateShellBase), data); err != nil {
return "", err
}
return buf.String(), nil
Expand Down
61 changes: 61 additions & 0 deletions core/engine/render/templates_paths.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package render

import "path/filepath"

// Relative paths under TemplatesPath (core/engine/templates).
const (
TemplateShellBase = "shell/base.html"

TemplatePartialMenuIcons = "partials/menu_icons.html"
TemplatePartialHomeHub = "partials/home_hub.html"
TemplatePartialAppsModule = "partials/apps_module.html"
TemplatePartialLoginBrand = "partials/login_brand_aside.html"
TemplatePartialAuthHead = "partials/auth_head.html"

TemplateAuthLogin = "auth/login.html"
TemplateAuthTOTP = "auth/totp_login.html"
TemplateAuthSetup = "auth/setup.html"

TemplatePortalHome = "portal/portal_home.html"
TemplatePortalShare = "portal/portal_share.html"

TemplatePagesHome = "pages/home_dashboard.html"
TemplatePagesApps = "pages/apps.html"
TemplatePagesAppLogs = "pages/app_logs.html"
TemplatePagesSettingsHub = "pages/settings_hub.html"
TemplatePagesSettingsAccount = "pages/settings_account.html"
TemplatePagesSettingsFieldACL = "pages/settings_field_acl.html"
TemplatePagesSettingsModelACL = "pages/settings_model_acl.html"
)

// TemplatePath joins templatesDir with a relative template path.
func TemplatePath(templatesDir, rel string) string {
return filepath.Join(templatesDir, rel)
}

// ShellLayoutTemplateFiles returns files parsed for base.html layout rendering.
func ShellLayoutTemplateFiles(templatesDir string) []string {
return []string{
TemplatePath(templatesDir, TemplateShellBase),
TemplatePath(templatesDir, TemplatePartialMenuIcons),
}
}

// ShellInnerTemplateFiles returns files parsed for a shell inner page (pages/*.html).
func ShellInnerTemplateFiles(templatesDir, pageRel string) []string {
return []string{
TemplatePath(templatesDir, pageRel),
TemplatePath(templatesDir, TemplatePartialMenuIcons),
TemplatePath(templatesDir, TemplatePartialHomeHub),
TemplatePath(templatesDir, TemplatePartialAppsModule),
}
}

// AuthTemplateFiles returns files parsed for standalone auth pages (login, TOTP, setup).
func AuthTemplateFiles(templatesDir, pageRel string) []string {
return []string{
TemplatePath(templatesDir, pageRel),
TemplatePath(templatesDir, TemplatePartialLoginBrand),
TemplatePath(templatesDir, TemplatePartialAuthHead),
}
}
17 changes: 17 additions & 0 deletions core/engine/templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Engine HTML templates

Root path: `core/engine/templates` (`templates_path` in config). Relative paths are defined in [`render/templates_paths.go`](../render/templates_paths.go).

| Folder | Role | Loaded by |
|--------|------|-----------|
| `shell/` | Authenticated app chrome (`base.html`) | `render.RenderPage` |
| `partials/` | Shared `{{define}}` fragments (menu icons, home hub, apps forms, login brand, auth assets) | Parsed with shell layout, shell inner pages, or auth pages |
| `pages/` | Main column HTML injected into `base.html` | `web.renderShellPage` / `executeInnerTemplate` |
| `auth/` | Standalone login, TOTP, setup wizards | `web.getAuthTemplate`, setup handler |
| `portal/` | Portal home and share pages | `web.loadPortalTemplates` |

## Parse sets

- **Shell layout:** `shell/base.html` + `partials/menu_icons.html`
- **Shell inner page:** `pages/<page>.html` + menu, home hub, and apps partials
- **Auth page:** `auth/<page>.html` + `partials/login_brand_aside.html` + `partials/auth_head.html`
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sign in — {{.AppName}}</title>
{{range .Stylesheets}}
<link rel="stylesheet" href="{{.}}">
{{end}}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap" rel="stylesheet">
{{template "auth_head_assets" .}}
</head>
<body class="sum-login-page">
<div class="sum-login-shell">
<aside class="sum-login-brand" aria-hidden="false">
<div class="sum-login-brand-inner">
{{if .LogoURL}}
<img src="{{.LogoURL}}" alt="" class="sum-login-brand-logo"/>
{{else}}
<div class="sum-login-logo sum-login-logo--placeholder sum-login-brand-logo" aria-hidden="true">S</div>
{{end}}
<p class="sum-login-brand-kicker">{{.AppName}} ERP</p>
<h2 class="sum-login-brand-title">{{if .CompanyName}}{{.CompanyName}}{{else}}Secure workspace access{{end}}</h2>
<p class="sum-login-brand-copy">Enterprise resource planning with role-based access, audit trails, and optional single sign-on.</p>
<ul class="sum-login-trust-list">
<li>Encrypted sessions</li>
<li>Multi-factor authentication</li>
<li>Administrator-controlled identity providers</li>
</ul>
</div>
</aside>
{{template "login_brand_aside" .}}
<main class="sum-login-panel">
<div class="sum-login-card sum-login-card--panel">
<h1>Sign in</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Database setup — Sumeru</title>
{{range .Stylesheets}}
<link rel="stylesheet" href="{{.}}">
{{end}}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap" rel="stylesheet">
{{template "auth_head_assets" .}}
</head>
<body class="sum-login-page">
<div class="sum-login-shell">
<aside class="sum-login-brand" aria-hidden="false">
<div class="sum-login-brand-inner">
<div class="sum-login-logo sum-login-logo--placeholder sum-login-brand-logo" aria-hidden="true">S</div>
<p class="sum-login-brand-kicker">Sumeru ERP</p>
<h2 class="sum-login-brand-title">Database setup</h2>
<p class="sum-login-brand-copy">Connect your PostgreSQL database, create your company, and define the first administrator account.</p>
</div>
</aside>
{{template "login_brand_aside" .}}
<main class="sum-login-panel">
<div class="sum-login-card sum-setup-card sum-setup-card--wide sum-login-card--panel">
<p class="sum-setup-progress" id="stepLabel" aria-live="polite">Step 1 of 4</p>
Expand Down
Loading
Loading