1
0
mirror of synced 2025-12-19 10:00:37 -05:00

first commit

This commit is contained in:
Omkar Yadav
2019-07-20 12:15:03 +05:30
commit 8d201f3502
42 changed files with 9868 additions and 0 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/node_modules
/carbon
.sonarlint
.idea
.docker-compose

11
Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
FROM node:10 as node
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build:prod
FROM busybox
COPY --from=node /usr/src/app/carbon /carbon

23
README.md Normal file
View File

@@ -0,0 +1,23 @@
# Carbon theme for the keycloak
[Carbon Design](https://carbondesignsystem.com/) for the [keycloak](https://www.keycloak.org)
Special thanks for reference [GOV.UK](https://github.com/UKHomeOffice/keycloak-theme-govuk).
## Screenshots
### Login Page
![Login Page](screenshots/login_page.png "Login page")
## TODO
- [ ] Login
- [x] Login
- [x] Register
- [x] Reset password
- [ ] TOTP
- [ ] OAuth Grant
- [ ] Config TOTP
- [ ] Account

13
docker-compose.yml Normal file
View File

@@ -0,0 +1,13 @@
version: '3.6'
services:
keycloak:
image: jboss/keycloak:latest
restart: always
ports:
- "8080:8080"
volumes:
- ./carbon/:/opt/jboss/keycloak/themes/carbon/
environment:
KEYCLOAK_PASSWORD: "admin"
KEYCLOAK_USER: "admin"
PROXY_ADDRESS_FORWARDING: "true"

7561
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

57
package.json Normal file
View File

@@ -0,0 +1,57 @@
{
"name": "keycloak-carbon-theme",
"version": "1.0.0",
"description": "WIP: Stay tune",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.dev.js",
"build:prod": "webpack --env prod --config webpack.config.js",
"watch": "webpack --watch --config webpack.config.dev.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/httpsOmkar/keycloak-carbon-theme.git"
},
"author": "Omkar Yadav <httpsOmkar@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/httpsOmkar/keycloak-carbon-theme/issues"
},
"homepage": "https://github.com/httpsOmkar/keycloak-carbon-theme#readme",
"devDependencies": {
"@babel/core": "^7.5.4",
"@babel/preset-env": "^7.5.4",
"@types/angular": "^1.6.55",
"@types/blueimp-md5": "^2.7.0",
"babel-loader": "^8.0.6",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.0.3",
"css-loader": "^3.0.0",
"dotenv-webpack": "^1.7.0",
"node-sass": "^4.12.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"terser-webpack-plugin": "^1.3.0",
"ts-loader": "^6.0.4",
"typescript": "^3.5.3",
"uglifyjs-webpack-plugin": "^2.1.3",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.5"
},
"dependencies": {
"angular": "^1.7.8",
"angular-cookies": "^1.7.8",
"angular-resource": "^1.7.8",
"angular-route": "^1.7.8",
"angular-sanitize": "^1.7.8",
"angular-translate": "^2.18.1",
"angular-translate-loader-url": "^2.18.1",
"angular-ui-select2": "0.0.5",
"autofill-event": "0.0.1",
"blueimp-md5": "^2.11.0",
"carbon-components": "^10.3.2",
"jquery": "^3.4.1",
"select2": "^4.0.7"
}
}

BIN
screenshots/login_page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

167
src/account/account.ftl Normal file
View File

@@ -0,0 +1,167 @@
<#import "template.ftl" as layout>
<@layout.mainLayout active='account' bodyClass='user'; section>
<h1 class="GreetingsMessage NoMargin">
${msg("editAccountHtmlTitle")}
</h1>
<form action="${url.accountUrl}" class="AccountForm" method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
<#if !realm.registrationEmailAsUsername>
<div class="bx--form-item bx--text-input-wrapper">
<label for="username" class="bx--label">${msg("username")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('username','data-invalid')}>
<#if messagesPerField.exists('username')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16"
height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="username" type="text"
class="bx--text-input ${ messagesPerField.printIfExists('username', 'bx--text-input--invalid') }"
placeholder="${ msg('username') }" <#if !realm.editUsernameAllowed>disabled="disabled"</#if>
name="username"
value="${(account.username!'')}">
</div>
<#if messagesPerField.exists('username')>
<div class="bx--form-requirement">${ messagesPerField.get('username') }</div>
</#if>
</div>
</#if>
<div class="bx--form-item bx--text-input-wrapper">
<label for="email" class="bx--label">${msg("email")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('email','data-invalid')}>
<#if messagesPerField.exists('email')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16" height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="email" type="text"
class="bx--text-input ${ messagesPerField.printIfExists('username', 'bx--text-input--invalid') }"
autofocus
name="email"
placeholder="${ msg('email') }"
value="${(account.email!'')}">
</div>
<#if messagesPerField.exists('email')>
<div class="bx--form-requirement">${ messagesPerField.get('email') }</div>
</#if>
</div>
<#-- <#if !realm.registrationEmailAsUsername>-->
<#-- <div class="form-group ${messagesPerField.printIfExists('username','has-error')}">-->
<#-- <div class="col-sm-2 col-md-2">-->
<#-- <label for="username" class="control-label"></label> <#if realm.editUsernameAllowed><span-->
<#-- class="required">*</span></#if>-->
<#-- </div>-->
<#-- <div class="col-sm-10 col-md-10">-->
<#-- <input type="text" class="form-control" id="username" name="username" "/>-->
<#-- </div>-->
<#-- </div>-->
<#-- </#if>-->
<div class="bx--form-item bx--text-input-wrapper">
<label for="firstName" class="bx--label">${msg("firstName")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('firstName','data-invalid')}>
<#if messagesPerField.exists('firstName')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16" height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="firstName" type="text"
class="bx--text-input ${ messagesPerField.printIfExists('username', 'bx--text-input--invalid') }"
autofocus
name="firstName"
placeholder="${ msg('firstName') }"
value="${(account.firstName!'')}">
</div>
<#if messagesPerField.exists('firstName')>
<div class="bx--form-requirement">${ messagesPerField.get('firstName') }</div>
</#if>
</div>
<div class="bx--form-item bx--text-input-wrapper">
<label for="lastName" class="bx--label">${msg("lastName")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('lastName','data-invalid')}>
<#if messagesPerField.exists('lastName')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16" height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="lastName" type="text"
class="bx--text-input ${ messagesPerField.printIfExists('username', 'bx--text-input--invalid') }"
autofocus
name="lastName"
placeholder="${ msg('lastName') }"
value="${(account.lastName!'')}">
</div>
<#if messagesPerField.exists('lastName')>
<div class="bx--form-requirement">${ messagesPerField.get('lastName') }</div>
</#if>
</div>
<#-- <div class="form-group ${messagesPerField.printIfExists('firstName','has-error')}">-->
<#-- <div class="col-sm-2 col-md-2">-->
<#-- <label for="firstName" class="control-label">${msg("firstName")}</label> <span class="required">*</span>-->
<#-- </div>-->
<#-- <div class="col-sm-10 col-md-10">-->
<#-- <input type="text" class="form-control" id="firstName" name="firstName"-->
<#-- value="${(account.firstName!'')}"/>-->
<#-- </div>-->
<#-- </div>-->
<#-- <div class="form-group ${messagesPerField.printIfExists('lastName','has-error')}">-->
<#-- <div class="col-sm-2 col-md-2">-->
<#-- <label for="lastName" class="control-label">${msg("lastName")}</label> <span class="required">*</span>-->
<#-- </div>-->
<#-- <div class="col-sm-10 col-md-10">-->
<#-- <input type="text" class="form-control" id="lastName" name="lastName" value="${(account.lastName!'')}"/>-->
<#-- </div>-->
<#-- </div>-->
<div class="DisplayFlex">
<#if url.referrerURI??>
<div>
<a role="button" class="bx--btn bx--btn--primary" type="button" href="${url.referrerURI}">
${kcSanitize(msg("backToApplication")?no_esc)}
</a>
</div>
</#if>
<div>
<button type="submit"
class="bx--btn bx--btn--primary"
name="submitAction" value="Save">${msg("doSave")}</button>
</div>
<div>
<button type="submit"
class="bx--btn bx--btn--tertiary"
name="submitAction" value="Cancel">${msg("doCancel")}</button>
</div>
</div>
</form>
</@layout.mainLayout>

View File

@@ -0,0 +1,110 @@
<#import "template.ftl" as layout>
<@layout.mainLayout active='applications' bodyClass='applications'; section>
<div class="DashboardProfilePictureWrapper">
<div class="DashboardProfilePicture">
<figure class="ProfileContent">
<img alt="Profile"
style="display: none"
id="profileImage"
width="96"
height="96">
</figure>
<div class="ChangeGravatar" onclick="handleChangeAvatar()">
<div></div>
</div>
</div>
</div>
<h1 class="GreetingsMessage" style="display: none" id="greetingsMessage">
Good <span id="afterTime"></span>, <b class="Username">${ account.firstName! }</b>
</h1>
<form action="${url.applicationsUrl}" method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
<input type="hidden" id="referrer" name="referrer" value="${stateChecker}">
<div class="CardList">
<#list applications.applications as application>
<#if application.effectiveUrl?has_content>
<a class="bx--tile bx--tile--clickable Card" href="${application.effectiveUrl}">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
class="Icon"
xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"
aria-hidden="true">
<path d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2zM6 6v10h10V6zm20 6v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2zm0 12v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2zm-10 2v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2z"></path>
</svg>
<span>
<#if application.client.name?has_content>${advancedMsg(application.client.name)}<#else>${application.client.clientId}</#if>
</span>
</a>
</#if>
</#list>
</div>
<#-- <table class="table table-striped table-bordered">-->
<#-- <thead>-->
<#-- <tr>-->
<#-- <td>${msg("application")}</td>-->
<#-- <td>${msg("availableRoles")}</td>-->
<#-- <td>${msg("grantedPermissions")}</td>-->
<#-- <td>${msg("additionalGrants")}</td>-->
<#-- <td>${msg("action")}</td>-->
<#-- </tr>-->
<#-- </thead>-->
<#-- <tbody>-->
<#-- <#list applications.applications as application>-->
<#-- <tr>-->
<#-- <td>-->
<#-- <#if application.effectiveUrl?has_content><a href="${application.effectiveUrl}"></#if>-->
<#-- <#if application.client.name?has_content>${advancedMsg(application.client.name)}<#else>${application.client.clientId}</#if>-->
<#-- <#if application.effectiveUrl?has_content></a></#if>-->
<#-- </td>-->
<#-- <td>-->
<#-- <#list application.realmRolesAvailable as role>-->
<#-- <#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if>-->
<#-- <#if role_has_next>, </#if>-->
<#-- </#list>-->
<#-- <#list application.resourceRolesAvailable?keys as resource>-->
<#-- <#if application.realmRolesAvailable?has_content>, </#if>-->
<#-- <#list application.resourceRolesAvailable[resource] as clientRole>-->
<#-- <#if clientRole.roleDescription??>${advancedMsg(clientRole.roleDescription)}<#else>${advancedMsg(clientRole.roleName)}</#if>-->
<#-- ${msg("inResource")}-->
<#-- <strong><#if clientRole.clientName??>${advancedMsg(clientRole.clientName)}<#else>${clientRole.clientId}</#if></strong>-->
<#-- <#if clientRole_has_next>, </#if>-->
<#-- </#list>-->
<#-- </#list>-->
<#-- </td>-->
<#-- <td>-->
<#-- <#if application.client.consentRequired>-->
<#-- <#list application.clientScopesGranted as claim>-->
<#-- ${advancedMsg(claim)}<#if claim_has_next>, </#if>-->
<#-- </#list>-->
<#-- <#else>-->
<#-- <strong>${msg("fullAccess")}</strong>-->
<#-- </#if>-->
<#-- </td>-->
<#-- <td>-->
<#-- <#list application.additionalGrants as grant>-->
<#-- ${advancedMsg(grant)}<#if grant_has_next>, </#if>-->
<#-- </#list>-->
<#-- </td>-->
<#-- <td>-->
<#-- <#if (application.client.consentRequired && application.clientScopesGranted?has_content) || application.additionalGrants?has_content>-->
<#-- <button type='submit'-->
<#-- class='${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!}'-->
<#-- id='revoke-${application.client.clientId}' name='clientId'-->
<#-- value="${application.client.id}">${msg("revoke")}</button>-->
<#-- </#if>-->
<#-- </td>-->
<#-- </tr>-->
<#-- </#list>-->
<#-- </tbody>-->
<#-- </table>-->
</form>
</@layout.mainLayout>

78
src/account/index.ts Normal file
View File

@@ -0,0 +1,78 @@
import './style.scss';
import md5 from 'blueimp-md5';
function handleChangeAvatar() {
window.open(`https://en.gravatar.com/site/login`, '_blank');
}
function setTime() {
const afterTime = document.getElementById('afterTime');
if (afterTime) {
afterTime.textContent = getTimeType();
}
const greetingsMessage = document.getElementById('greetingsMessage');
if (greetingsMessage) {
greetingsMessage.style.display = 'inherit';
}
}
export function getTimeType() {
const today = new Date();
const curHr = today.getHours();
if (curHr < 12) {
return 'morning';
} else if (curHr < 18) {
return 'afternoon';
} else {
return 'evening';
}
}
window.addEventListener('load', () => {
setTime();
const sideNavigation = document.getElementById('sideNavigation');
if (sideNavigation) {
const mediaQueryList = window.matchMedia('(max-width: 66rem)');
const initToggle = () => {
if (mediaQueryList.matches) {
sideNavigation.classList.remove('bx--side-nav--expanded');
} else {
sideNavigation.classList.add('bx--side-nav--expanded');
}
};
const loadProfile = () => {
const profileImage = document.getElementById('profileImage');
if (profileImage) {
profileImage['src'] = `https://www.gravatar.com/avatar/${ md5(window['f4erp'].user.email) }?d=retro&amp;r=g`
profileImage.style.display = 'inherit';
}
};
initToggle();
loadProfile();
mediaQueryList.addEventListener('change', () => {
initToggle();
});
}
});
function toggleMenu(isOpen = false) {
const sideNavigation = document.getElementById('sideNavigation');
if (sideNavigation) {
if (sideNavigation.classList.contains('bx--side-nav--expanded') && isOpen !== true) {
sideNavigation.classList.remove('bx--side-nav--expanded');
} else {
sideNavigation.classList.add('bx--side-nav--expanded');
}
}
}
window['toggleMenu'] = toggleMenu;
window['handleChangeAvatar'] = handleChangeAvatar;

116
src/account/password.ftl Normal file
View File

@@ -0,0 +1,116 @@
<#import "template.ftl" as layout>
<@layout.mainLayout active='password' bodyClass='password'; section>
<h1 class="GreetingsMessage NoMargin">
${msg("changePasswordHtmlTitle")}
</h1>
<form action="${url.passwordUrl}" class="form-horizontal AccountForm" method="post">
<input type="text" id="username" name="username"
value="${(account.username!'')}"
autocomplete="username"
readonly="readonly" style="display:none;">
<#if password.passwordSet>
<div class="bx--form-item bx--text-input-wrapper">
<label for="password" class="bx--label">${msg("password")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('password','data-invalid')}>
<#if messagesPerField.exists('password')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16"
height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="password"
class="bx--text-input ${ messagesPerField.printIfExists('password', 'bx--text-input--invalid') }"
type="password"
placeholder="${ msg('password') }"
name="password"
autocomplete="current-password"
value="${(account.password!'')}">
</div>
<#if messagesPerField.exists('password')>
<div class="bx--form-requirement">${ messagesPerField.get('password') }</div>
</#if>
</div>
</#if>
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
<div class="bx--form-item bx--text-input-wrapper">
<label for="passwordNew" class="bx--label">${msg("passwordNew")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('password','data-invalid')}>
<#if messagesPerField.exists('password')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16"
height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="passwordNew"
class="bx--text-input ${ messagesPerField.printIfExists('password', 'bx--text-input--invalid') }"
type="password"
placeholder="${ msg('password') }"
name="password-new"
autocomplete="current-password"
value="${(account.password!'')}">
</div>
<#if messagesPerField.exists('password')>
<div class="bx--form-requirement">${ messagesPerField.get('password') }</div>
</#if>
</div>
<div class="bx--form-item bx--text-input-wrapper">
<label for="password-confirm" class="bx--label">${msg("passwordConfirm")}</label>
<div class="bx--text-input__field-wrapper" ${messagesPerField.printIfExists('passwordConfirm','data-invalid')}>
<#if messagesPerField.exists('password')>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--text-input__invalid-icon" width="16"
height="16"
viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 1C4.2 1 1 4.2 1 8s3.2 7 7 7 7-3.1 7-7-3.1-7-7-7zm-.5 3h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"></path>
<path d="M7.5 4h1v5h-1V4zm.5 8.2c-.4 0-.8-.4-.8-.8s.3-.8.8-.8c.4 0 .8.4.8.8s-.4.8-.8.8z"
data-icon-path="inner-path" opacity="0"></path>
</svg>
</#if>
<input id="password-confirm"
class="bx--text-input ${ messagesPerField.printIfExists('password-confirm', 'bx--text-input--invalid') }"
type="password"
placeholder="${ msg('passwordConfirm') }"
name="password-confirm"
autocomplete="current-password">
</div>
<#if messagesPerField.exists('password')>
<div class="bx--form-requirement">${ messagesPerField.get('password') }</div>
</#if>
</div>
<#-- <div class="form-group">-->
<#-- <div class="col-sm-2 col-md-2">-->
<#-- <label for="password-confirm" class="control-label" class="two-lines">${msg("passwordConfirm")}</label>-->
<#-- </div>-->
<#-- <div class="col-sm-10 col-md-10">-->
<#-- <input type="password" class="form-control" id="password-confirm" name="password-confirm"-->
<#-- autocomplete="new-password">-->
<#-- </div>-->
<#-- </div>-->
<div class="FlexDisplay">
<button type="submit"
class="bx--btn bx--btn--primary ${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}"
name="submitAction" value="Save">
${msg("doSave")}
</button>
</div>
</form>
</@layout.mainLayout>

View File

33
src/account/style.scss Normal file
View File

@@ -0,0 +1,33 @@
$app-page-background: #ECECEC;
$app-toolbar-background: #FFF;
@import "~carbon-components/css/carbon-components.css";
@import "style/dashboard";
@import "style/common";
@import "style/account";
html {
font-size: 20px;
}
body {
padding: 0;
margin: 0;
}
.SideBar {
border-right: 1px #EEE solid;
}
.PageTitle {
text-align: center;
font-size: 1.1rem;
margin-top: 2rem;
}
.FixedNotification {
position: fixed;
bottom: 0;
left: 12px;
z-index: 999999;
}

View File

@@ -0,0 +1,5 @@
.AccountForm {
.bx--form-item, .DisplayFlex, .FlexDisplay {
margin-top: 1rem;
}
}

View File

@@ -0,0 +1,73 @@
.CardList {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 1rem;
margin-top: 1.5rem;
@media (max-width: 399px) {
grid-template-columns: repeat(1, 1fr);
}
@media (min-width: 400px) and (max-width: 599px) {
grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 600px) and (max-width: 1099px) {
grid-template-columns: repeat(3, 1fr);
}
@media (min-width: 1100px) and (max-width: 1399px) {
grid-template-columns: repeat(4, 1fr);
}
@media (min-width: 1400px) {
grid-template-columns: repeat(5, 1fr);
}
.Card {
padding: 1.75rem;
cursor: pointer;
display: flex;
align-items: center;
place-content: center;
min-height: 6rem;
transition: all 0.125s ease-in-out, transform 0.125s ease-in-out;
flex-direction: column;
user-select: none;
text-decoration: none;
span {
color: #000;
text-decoration: none;
}
.Icon {
margin-bottom: 1rem;
}
&:hover,
&:active,
&:focus {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.1);
background-color: white;
}
}
}
.FlexLayout, .FlexDisplay, .DisplayFlex {
display: flex;
@media (min-width: 66rem) {
> * {
margin-right: .4rem;
}
}
@media (max-width: 66rem) {
flex-direction: column;
> * {
margin-bottom: .8rem;
}
}
}

View File

@@ -0,0 +1,71 @@
.DashboardProfilePictureWrapper {
display: flex;
justify-content: center;
align-items: center;
}
.DashboardProfilePicture {
flex-grow: 0;
flex-shrink: 0;
border-radius: 50%;
border: 1px solid #dadce0;
box-sizing: border-box;
overflow: hidden;
position: relative;
}
.ProfileContent {
align-items: center;
display: flex;
justify-content: center;
margin: 0;
overflow: hidden;
padding: 0;
border-radius: 50%;
width: 96px;
height: 96px;
img {
border-radius: 50%;
height: 100%;
width: auto;
}
}
.ChangeGravatar {
transition: opacity .2s ease-in-out;
background-color: rgba(32, 33, 36, 0.6);
bottom: 0;
height: 33%;
left: 0;
position: absolute;
right: 0;
opacity: 0;
&:hover {
opacity: 1;
}
div {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAB70lEQVRoge2Yz0oCQQCHN0V8h4ylLuE9L0bQE0TQXwSFugVCnXqCLl56CDGKhDB7HgnsAUJSyujP12FWmKZVZ3RtFpwP9iLz2/l9y8zuup7ncDimBjjHnHPbvT3P8zzgeILyAN/Ake3y28DnhAIAH8CWrfKbwNsU5Qe8AhtRl8sC98BLBAWnpQc8AFmT8h27nUPpoCOBuPJxpakjEIdlM4yujkCscQJj6ANVYAfwgVRw+MFvtWBMLAVugEWN82eAepwEvoCTsSf+O085yFoX+FMeSALriPekYyAPJIdIWBW4UbILQAl4ChnbBgoh8xktpygF+khrPih/qZGrKPMtAe82BKpKrmSQPVCyVzYEdqRMkvBlM4xHICHl92wI+FJm3SA3ICfll3VDUQqkpMwk/8qKUj6tG3ICEvISyhvXhzUpv6IbilJA3cRtg2yL35t434ZATckVDLK7SvbahkAfyCjZikbuQsn4WHqQAdyG5A8R93mVFsqVD8bfmUwYtQBAOeQcCSAHFINjDWnNS+NOTSebhcBXmITGPGfE5HV6QB1Y0ji/j+Gy+S8BEJvxCvFus4x4QKUR9/l9xN1Ge8PaEJg5cyHQs11yBFofth5stxxBQ0cgrh93n4HVsQKSRBPo2u0MiA4NdMs7HA6HY674AcVRw/E55G45AAAAAElFTkSuQmCC");
background-position: center;
background-repeat: no-repeat;
background-size: 24px 24px;
height: 100%;
opacity: .8;
cursor: pointer;
}
}
.GreetingsMessage {
text-align: center;
font-size: 1.1rem;
&:not(.NoMargin) {
margin-top: 2rem;
}
.Username {
font-weight: bold;
}
}

274
src/account/template.ftl Normal file
View File

@@ -0,0 +1,274 @@
<#macro mainLayout active bodyClass>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${ realm.displayName } Accounts</title>
<link rel="icon" href="${url.resourcesPath}/img/favicon.ico">
<#if properties.styles?has_content>
<#list properties.styles?split(' ') as style>
<link href="${url.resourcesPath}/${style}" rel="stylesheet"/>
</#list>
</#if>
<#if properties.scripts?has_content>
<#list properties.scripts?split(' ') as script>
<script type="text/javascript" src="${url.resourcesPath}/${script}"></script>
</#list>
</#if>
<script>
window.f4erp = window.f4erp || {};
window.f4erp.user = window.f4erp.user || {};
window.f4erp.user.email = '${(account.email!'')}';
window.f4erp.user.firstName = '${(account.firstName!'')}';
window.f4erp.user.lastName = '${(account.lastName!'')}';
window.f4erp.user.username = '${(account.username!'')}';
</script>
</head>
<body class="admin-console user ${bodyClass}">
<header aria-label="Header" class="bx--header bx--header--website" role="banner">
<a class="bx--skip-to-content"
href="#main-content"
tabindex="0">
Skip to main content
</a>
<button aria-label="Open menu"
class="bx--header__action--menu bx--header__action bx--header__menu-trigger bx--header__menu-toggle bx--header__menu-toggle__hidden"
onclick="toggleMenu()"
title="Open menu" type="button">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true">
<path d="M2 14.8h16V16H2zm0-3.6h16v1.2H2zm0-3.6h16v1.2H2zM2 4h16v1.2H2z"></path>
</svg>
</button>
<a class="bx--header__name" href="/">${ realm.displayName }&nbsp;<span>Accounts</span></a>
<div class="bx--header__global">
<#if referrer?has_content && referrer.url?has_content>
<button role="button" onclick="window.location = '${referrer.url}'"
title="${msg("backTo",referrer.name)}"
aria-label="${msg("backTo",referrer.name)}" class="bx--header__action" type="button">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"
aria-hidden="true">
<path d="M15.5 15.5H18V18h-2.5zm-6.75 0h2.5V18h-2.5zM2 15.5h2.5V18H2zm13.5-6.75H18v2.5h-2.5zm-6.75 0h2.5v2.5h-2.5zM2 8.75h2.5v2.5H2zM15.5 2H18v2.5h-2.5zM8.75 2h2.5v2.5h-2.5zM2 2h2.5v2.5H2z"></path>
</svg>
</button>
</#if>
<button aria-label="Logout" class="bx--header__action" type="button"
onclick="window.location = '${url.logoutUrl?esc}'">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true">
<path d="M6 30h12a2.002 2.002 0 0 0 2-2v-3h-2v3H6V4h12v3h2V4a2.002 2.002 0 0 0-2-2H6a2.002 2.002 0 0 0-2 2v24a2.002 2.002 0 0 0 2 2z"></path>
<path d="M20.586 20.586L24.172 17H10v-2h14.172l-3.586-3.586L22 10l6 6-6 6-1.414-1.414z"></path>
</svg>
</button>
</div>
</header>
<nav class="bx--side-nav__navigation bx--side-nav SideBar" aria-label="Side navigation" id="sideNavigation">
<script>
var mediaQueryList = window.matchMedia('(max-width: 66rem)');
if (mediaQueryList.matches) {
document.getElementById('sideNavigation').classList.remove('bx--side-nav--expanded');
} else {
document.getElementById('sideNavigation').classList.add('bx--side-nav--expanded');
}
</script>
<ul class="bx--side-nav__items">
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='applications'>active</#if>" href="${url.applicationsUrl}"
<#if active=='applications'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("applications")}</span>
</a>
</li>
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='account'>active</#if>" href="${url.accountUrl}"
<#if active=='account'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("account")}</span>
</a>
</li>
<#if features.passwordUpdateSupported>
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='password'>active</#if>" href="${url.passwordUrl}"
<#if active=='password'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("password")}</span>
</a>
</li>
</#if>
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='totp'>active</#if>" href="${url.totpUrl}"
<#if active=='totp'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("authenticator")}</span>
</a>
</li>
<#if features.identityFederation>
<li class="bx--side-nav__item">
<a href="${url.socialUrl}" class="bx--side-nav__link <#if active=='social'>active</#if>"
<#if active=='social'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("federatedIdentity")}</span>
</a>
</li>
</#if>
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='sessions'>active</#if>" href="${url.sessionsUrl}"
<#if active=='sessions'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("sessions")}</span>
</a>
</li>
<#if features.log>
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='log'>active</#if>" href="${url.logUrl}"
<#if active=='log'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("log")}</span>
</a>
</li>
</#if>
<#if realm.userManagedAccessAllowed && features.authorization>
<li class="bx--side-nav__item">
<a class="bx--side-nav__link <#if active=='authorization'>active</#if>" href="${url.resourceUrl}" <#if active=='authorization'>aria-current="page"</#if>>
<span class="bx--side-nav__link-text">${msg("myResources")}</span>
</a>
</li>
</#if>
</ul>
</nav>
<#if message?has_content>
<div data-notification
id="notificationSnackbar"
class="bx--inline-notification <#if message.type=='success' >bx--inline-notification--success</#if> <#if message.type=='error' >bx--inline-notification--error</#if> FixedNotification"
role="alert">
<div class="bx--inline-notification__details">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--inline-notification__icon" width="20" height="20"
viewBox="0 0 20 20" aria-hidden="true">
<path d="M10 1c-5 0-9 4-9 9s4 9 9 9 9-4 9-9-4-9-9-9zm3.5 13.5l-8-8 1-1 8 8-1 1z"></path>
<path d="M13.5 14.5l-8-8 1-1 8 8-1 1z" data-icon-path="inner-path" opacity="0"></path>
</svg>
<div class="bx--inline-notification__text-wrapper">
<p class="bx--inline-notification__title">${kcSanitize(message.summary)?no_esc}</p>
</div>
</div>
<button data-notification-btn class="bx--inline-notification__close-button" type="button" aria-label="close"
onclick="document.getElementById('notificationSnackbar').style.display = 'none'">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--inline-notification__close-icon" width="16"
height="16" viewBox="0 0 16 16" aria-hidden="true">
<path d="M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8z"></path>
</svg>
</button>
</div>
</#if>
<div class="bx--content" id="main-content">
<noscript>
This site require javascript
</noscript>
<#nested "content">
</div>
<#-- <aside class="bx--side-nav">-->
<#-- <div class="bx-container">-->
<#-- </div>-->
<#--&lt;#&ndash; <header class="navbar navbar-default navbar-pf navbar-main header">&ndash;&gt;-->
<#--&lt;#&ndash; <nav class="navbar" role="navigation">&ndash;&gt;-->
<#--&lt;#&ndash; <div class="navbar-header">&ndash;&gt;-->
<#--&lt;#&ndash; <div class="container">&ndash;&gt;-->
<#--&lt;#&ndash; <h1 class="navbar-title">Keycloak</h1>&ndash;&gt;-->
<#--&lt;#&ndash; </div>&ndash;&gt;-->
<#--&lt;#&ndash; </div>&ndash;&gt;-->
<#--&lt;#&ndash; <div class="navbar-collapse navbar-collapse-1">&ndash;&gt;-->
<#--&lt;#&ndash; <div class="container">&ndash;&gt;-->
<#--&lt;#&ndash; <ul class="nav navbar-nav navbar-utility">&ndash;&gt;-->
<#--&lt;#&ndash; <#if realm.internationalizationEnabled>&ndash;&gt;-->
<#--&lt;#&ndash; <li>&ndash;&gt;-->
<#--&lt;#&ndash; <div class="kc-dropdown" id="kc-locale-dropdown">&ndash;&gt;-->
<#--&lt;#&ndash; <a href="#" id="kc-current-locale-link">${locale.current}</a>&ndash;&gt;-->
<#--&lt;#&ndash; <ul>&ndash;&gt;-->
<#--&lt;#&ndash; <#list locale.supported as l>&ndash;&gt;-->
<#--&lt;#&ndash; <li class="kc-dropdown-item"><a href="${l.url}">${l.label}</a></li>&ndash;&gt;-->
<#--&lt;#&ndash; </#list>&ndash;&gt;-->
<#--&lt;#&ndash; </ul>&ndash;&gt;-->
<#--&lt;#&ndash; </div>&ndash;&gt;-->
<#--&lt;#&ndash; <li>&ndash;&gt;-->
<#--&lt;#&ndash; </#if>&ndash;&gt;-->
<#--&lt;#&ndash; <#if referrer?has_content && referrer.url?has_content>&ndash;&gt;-->
<#-- <li><a href="${referrer.url}" id="referrer">${msg("backTo",referrer.name)}</a></li></#if>-->
<#--&lt;#&ndash; <li><a href="${url.logoutUrl}">${msg("doSignOut")}</a></li>&ndash;&gt;-->
<#--&lt;#&ndash; </ul>&ndash;&gt;-->
<#--&lt;#&ndash; </div>&ndash;&gt;-->
<#--&lt;#&ndash; </div>&ndash;&gt;-->
<#--&lt;#&ndash; </nav>&ndash;&gt;-->
<#--&lt;#&ndash; </header>&ndash;&gt;-->
<#-- <div class="container">-->
<#--&lt;#&ndash; <div class="bs-sidebar col-sm-3">&ndash;&gt;-->
<#--&lt;#&ndash; <ul>&ndash;&gt;-->
<#-- <li class="<#if active=='account'>active</#if>">-->
<#-- <a href="${url.accountUrl}">${msg("account")}</a>-->
<#-- </li>-->
<#-- <#if features.passwordUpdateSupported>-->
<#-- <li class="<#if active=='password'>active</#if>">-->
<#-- <a href="${url.passwordUrl}">${msg("password")}</a>-->
<#-- </li>-->
<#-- </#if>-->
<#-- <li class="<#if active=='totp'>active</#if>">-->
<#-- <a href="${url.totpUrl}">${msg("authenticator")}</a>-->
<#-- </li>-->
<#-- <#if features.identityFederation>-->
<#-- <li class="<#if active=='social'>active</#if>">-->
<#-- <a href="${url.socialUrl}">${msg("federatedIdentity")}</a>-->
<#-- </li>-->
<#-- </#if>-->
<#-- <li class="<#if active=='sessions'>active</#if>">-->
<#-- <a href="${url.sessionsUrl}">${msg("sessions")}</a>-->
<#-- </li>-->
<#-- <li class="<#if active=='applications'>active</#if>">-->
<#-- <a href="${url.applicationsUrl}">${msg("applications")}</a>-->
<#-- </li>-->
<#-- <#if features.log>-->
<#-- <li class="<#if active=='log'>active</#if>">-->
<#-- <a href="${url.logUrl}">${msg("log")}</a>-->
<#-- </li>-->
<#-- </#if>-->
<#-- <#if realm.userManagedAccessAllowed && features.authorization>-->
<#-- <li class="<#if active=='authorization'>active</#if>">-->
<#-- <a href="${url.resourceUrl}">${msg("myResources")}</a>-->
<#-- </li>-->
<#-- </#if>-->
<#-- &lt;#&ndash;&lt;#&ndash; </ul>&ndash;&gt;&ndash;&gt;-->
<#-- &lt;#&ndash;&lt;#&ndash; </div>&ndash;&gt;&ndash;&gt;-->
<#-- &lt;#&ndash; <div class="col-sm-9 content-area">&ndash;&gt;-->
<#-- <#if message?has_content>-->
<#-- <div class="alert alert-${message.type}">-->
<#-- <#if message.type=='success' ><span class="pficon pficon-ok"></span></#if>-->
<#-- <#if message.type=='error' ><span class="pficon pficon-error-octagon"></span><span-->
<#-- class="pficon pficon-error-exclamation"></span></#if>-->
<#-- ${kcSanitize(message.summary)?no_esc}-->
<#-- </div>-->
<#-- </#if>-->
<#-- <div class="bx--content">-->
<#-- </div>-->
<#-- </div>-->
<#-- </div>-->
</body>
</html>
</#macro>

View File

@@ -0,0 +1,14 @@
parent=base
import=common/keycloak
styles=
scripts=js/bundle.js
##### css classes for form buttons
# main class used for all buttons
kcButtonClass=btn
# classes defining priority of the button - primary or default (there is typically only one priority button for the form)
kcButtonPrimaryClass=btn-primary
kcButtonDefaultClass=btn-default
# classes defining size of the button
kcButtonLargeClass=btn-lg

View File

@@ -0,0 +1,3 @@
export function test() {
return 'test';
}

1
src/admin/index.ts Normal file
View File

@@ -0,0 +1 @@
import './style.scss';

View File

@@ -0,0 +1,431 @@
html,body {
height: 100%;
}
form {
margin-top: 20px;
}
table {
margin-top: 20px;
}
.required {
color: #f00;
}
.tooltip-inner {
min-width: 200px;
}
.margin-top {
margin-top: 20px;
}
.no-margin-top {
margin-top: 0px !important;
}
table {
max-width: 100%;
}
td.clip {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 0;
}
th.w-10 {
width: 10%;
}
th.w-15 {
width: 15%;
}
th.w-20 {
width: 20%;
}
th.w-25 {
width: 25%;
}
th.w-30 {
width: 30%;
}
th.w-35 {
width: 35%;
}
th.w-40 {
width: 40%;
}
/*********** Loading ***********/
.loading {
background-color: #f5f5f5;
border: 1px solid #eee;
position: absolute;
bottom: 0px;
left: 0px;
padding: 2px 200px 2px 5px;
}
/*********** Feedback ***********/
.feedback-aligner {
position: fixed;
top: 15px;
text-align: center;
width: 100%;
height: 0;
z-index: 100;
}
.feedback-aligner .alert {
border-radius: 2px;
border-width: 1px;
display: inline-block;
position: relative;
}
/*********** On-Off Switch ***********/
.onoffswitch {
-moz-user-select: none;
height: 26px;
position: relative;
width: 62px;
}
.onoffswitch .onoffswitch-checkbox {
display: none;
}
.onoffswitch .onoffswitch-label {
border: 1px solid #bbb;
border-radius: 2px;
cursor: pointer;
display: block;
overflow: hidden;
width: 62px;
}
.onoffswitch .onoffswitch-inner {
display: block;
margin-left: -100%;
transition: margin 0.3s ease-in 0s;
width: 200%;
}
.onoffswitch .onoffswitch-inner > span {
-moz-box-sizing: border-box;
color: white;
float: left;
font-size: 11px;
font-family: "Open Sans", sans-serif;
font-weight: bold;
height: 24px;
line-height: 24px;
padding: 0;
width: 50%;
}
.onoffswitch .onoffswitch-switch {
background-image: linear-gradient(top, #fafafa 0%, #ededed 100%);
background-image: -o-linear-gradient(top, #fafafa 0%, #ededed 100%);
background-image: -moz-linear-gradient(top, #fafafa 0%, #ededed 100%);
background-image: -webkit-linear-gradient(top, #fafafa 0%, #ededed 100%);
background-image: -ms-linear-gradient(top, #fafafa 0%, #ededed 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fafafa), color-stop(1, 0, #ededed));
border: 1px solid #aaa;
border-radius: 2px;
bottom: 0;
margin: 0;
position: absolute;
right: 39px;
top: 0;
transition: all 0.3s ease-in 0s;
-webkit-transition: all 0.3s ease-in 0s;
width: 23px;
}
.onoffswitch .onoffswitch-inner .onoffswitch-active {
background-image: linear-gradient(top, #00a9ec 0%, #009bd3 100%);
background-image: -o-linear-gradient(top, #00a9ec 0%, #009bd3 100%);
background-image: -moz-linear-gradient(top, #00a9ec 0%, #009bd3 100%);
background-image: -webkit-linear-gradient(top, #00a9ec 0%, #009bd3 100%);
background-image: -ms-linear-gradient(top, #00a9ec 0%, #009bd3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #00a9ec), color-stop(1, 0, #009bd3));
color: #FFFFFF;
padding-left: 10px;
}
.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner .onoffswitch-active,
.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner .onoffswitch-inactive {
background-image: none;
background-color: #e5e5e5;
color: #9d9fa1;
}
.onoffswitch .onoffswitch-inner .onoffswitch-inactive {
background: linear-gradient(#fefefe, #e8e8e8) repeat scroll 0 0 transparent;
color: #4d5258;
padding-right: 10px;
text-align: right;
}
.onoffswitch .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
margin-left: 0;
}
.onoffswitch .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
right: 0;
}
/*********** Select 2 ***********/
.select2-container {
width: 100%;
}
.select2-container-multi .select2-choices .select2-search-field {
height: 26px;
}
/*********** html select ********/
.overflow-select {
overflow: auto;
}
/*********** New Menu ***********/
.sidebar-pf-left{
background: #292e34;
}
.sidebar-pf .nav-pills > li a i, .sidebar-pf .nav-pills > li a span{
color: #72767b;
display: inline-block;
margin-right: 10px;
}
.sidebar-pf .nav-pills > li > a{
color: #dbdada;
padding: 0px 20px 0 30px!important;
line-height: 30px;
border-left-width: 12px;
border-left-style: solid;
border-left-color: #292e34;
margin-left: -6px;
}
.sidebar-pf .nav-pills > li > a:hover{
background: #393f44;
border-color:#292e34;
border-left-color: #393f44;
color: #fff;
}
.sidebar-pf .nav-pills > li > a:after{
display: none!important;
}
.sidebar-pf .nav-pills > li.active > a {
color: #fff;
background: #393f44!important;
border-bottom: 1px solid #000!important;
border-top: 1px solid #000!important;
border-left-color: #39a5dc!important;
}
.sidebar-pf .nav-pills > li.active a i, .sidebar-pf .nav-pills > li.active a span{
color: #39a5dc;
}
/*********** Realm selector ***********/
.realm-selector{
color: #fff;
margin: 0 -20px;
position: relative;
}
.realm-dropmenu{
display: none;
cursor: pointer;
position: absolute;
top: 60px;
left: 0;
right: 0;
z-index: 999;
background: #fff;
}
.realm-selector:hover .realm-dropmenu{
display: block;
}
.realm-add{
padding: 10px;
}
.realm-selector h2{
font-size: 16px;
line-height: 60px;
padding: 0 20px;
margin: 0;
border-bottom: 1px solid #d5d5d6;
}
.realm-selector h2 i{
display: inline-block;
float: right;
line-height: 60px;
}
.realm-selector ul{
padding-left: 0;
margin: 0;
list-style: none;
max-height: 200px;
overflow-y:auto;
}
.realm-selector ul li a{
line-height: 60px;
padding: 0 20px;
border-bottom: 1px solid #d5d5d6;
line-height: 39px;
display: block;
font-size: 14px;
}
/*********** Overwrites header defaults ***********/
.navbar-pf{
border-top: none!important;
}
.navbar-pf .navbar-brand {
padding: 0;
height: 56px;
line-height: 56px;
background-position: center center;
background-image: url('../img/keyclok-logo.png');
background-size: 148px 30px;
background-repeat: no-repeat;
width: 148px;
}
.navbar-pf .navbar-utility > li > a{
padding: 22px 30px 23px 40px!important;
}
/* @media (min-width: 768px) */
.navbar-pf .navbar-utility li.dropdown > .dropdown-toggle .pficon-user {
top: 22px;
left:20px;
}
.clickable {
cursor: pointer;
}
h1 i {
color: #999999;
font-size: 18px;
margin-left: 10px;
}
/* Action cell */
.kc-action-cell {
background-color: #eeeeee;
background-image: linear-gradient(to bottom, #fafafa 0%, #ededed 100%);
background-repeat: repeat-x;
text-align: center;
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor:pointer;
}
.kc-action-cell:hover {
background-color: #eeeeee;
background-image: none;
}
.kc-sorter span {
margin-left: 10px;
}
/* Time selector */
.time-selector input {
display: inline-block;
width: 120px;
padding-right: 0;
margin-right: 0;
}
.time-selector select {
display: inline-block;
width: 80px;
margin-left: 0;
padding-left: 0;
}
.ace_editor {
height: 600px;
width: 100%;
}
.kc-button-input-file input {
float: left;
width: 73%;
}
.kc-button-input-file label {
float: left;
margin-left: 2%;
width: 25%;
}
table.kc-authz-table-expanded {
margin-top: 0px !important;
}
.no-gutter > [class*='col-'] {
padding-right:0!important;
padding-left:0!important;
}
.password-conceal {
font-family: 'text-security-disc';
font-size: 14px;
}
/* Deactivation styles for user-group membership tree models */
div[tree-model] li .deactivate {
color: #4a5053;
opacity: 0.4;
}
div[tree-model] li .deactivate_selected {
background-color: #dcdcdc;
font-weight: bold;
padding: 1px 5px;
}
/* search highlighting */
div[tree-model] li .highlight {
background-color: #aaddff;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="-145.2 359 325.4 77.2" enable-background="new -145.2 359 325.4 77.2" xml:space="preserve">
<g>
<g>
<path fill="none" stroke="#FFFFFF" stroke-width="7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M-79.1,393.1c-7.2,5-14.7,3.7-23.2-3.4c-4.9-4.1-11.2-2.8-15.4-1.5c-2.6,0.8-6.3,0.7-9.5,0.1c-14-2.9-10.9,6-8.3,14
c4.8,14.6-3.2,18-6,12.8c-1.8,6.6,7.1,8.6,10.9,7.7c6-1.4,14.1-2.2,19-0.6c9.5,2.9,13.1,0.5,11.5-2.6c-0.5-1-0.2-2.4,0.6-3.1
c2.8-2.4,5.3-0.4,8-1.9c2.1-1.2,2.1-3.9-1.2-5.5c-1.7-0.8-1.6-2.9,0.2-3.8c3.7-2,8.8-1,10-5.1C-81.9,398.7-80.7,395-79.1,393.1z"
/>
<path fill="none" stroke="#FFFFFF" stroke-width="7" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M-112.4,367.4c-1.1-0.6-4.3-2.6-5.1-3.1c-0.8-0.5-2.7-1.9-4.8-1.9s-4,1.4-4.8,1.9c-0.8,0.5-4,2.5-5.1,3.1c-0.8,0.5-3.7,2-3.7,6.3
c0,1.3,0,3.6,0,4.5c0,2.8,1.6,5,4.5,6.4c1,0.5,1.8,1.3,1.8,3.5c0,0.9,0.4,1.2,0.9,1.2h0.7c0.6,0,1,0.2,1,1.3c0,0.6,0,2.1,0,2.1
v37.2l2.7,2.8h1.2l5.7-5.8v-34.2c0,0,0-1.5,0-2.1c0-1,0.3-1.3,1-1.3h0.7c0.6,0,0.9-0.2,0.9-1.2c0-2.1,0.8-3,1.8-3.5
c2.9-1.5,4.5-3.6,4.5-6.4c0-1,0-3.2,0-4.5C-108.7,369.5-111.6,367.9-112.4,367.4z"/>
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-227.7191" y1="2220.7964" x2="-165.056" y2="2220.7964" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FDFDFD"/>
<stop offset="0.1452" style="stop-color:#F4F5F5"/>
<stop offset="0.6844" style="stop-color:#D9DBDB"/>
<stop offset="1" style="stop-color:#CFD1D1"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M-79.1,393.1c-7.2,5-14.7,3.7-23.2-3.4c-4.9-4.1-11.2-2.8-15.4-1.5c-2.6,0.8-6.3,0.7-9.5,0.1
c-14-2.9-10.9,6-8.3,14c4.8,14.6-3.2,18-6,12.8c-1.8,6.6,7.1,8.6,10.9,7.7c6-1.4,14.1-2.2,19-0.6c9.5,2.9,13.1,0.5,11.5-2.6
c-0.5-1-0.2-2.4,0.6-3.1c2.8-2.4,5.3-0.4,8-1.9c2.1-1.2,2.1-3.9-1.2-5.5c-1.7-0.8-1.6-2.9,0.2-3.8c3.7-2,8.8-1,10-5.1
C-81.9,398.7-80.7,395-79.1,393.1z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="192.3203" y1="1507.8835" x2="184.0467" y2="1507.8835" gradientTransform="matrix(1 0 0 1 -302 -1102)">
<stop offset="0" style="stop-color:#E6E6E6"/>
<stop offset="1" style="stop-color:#B2B3B3"/>
</linearGradient>
<path fill="url(#SVGID_2_)" d="M-115,391.3c0,5,0.7,22.8,4.5,30.4l-8.7-0.2v-31.5L-115,391.3z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-200.8627" y1="2227.3811" x2="-193.8624" y2="2207.8804" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#F6F6F6"/>
<stop offset="1" style="stop-color:#B2B3B3"/>
</linearGradient>
<path fill="url(#SVGID_3_)" d="M-134.6,403.1c-2.8-7.5-6.5-16.6,7.5-13.7c3.2,0.7,6.9,0.7,9.5-0.1c4.3-1.3,10-2.4,14.5,1.5
c8.9,7.7,16.9,8.3,24.1,3.3c-1.6,1.9-3.1,5.6-3.3,7.1c-0.2,1.3-1.1,1.9-2.4,2.4c-18.5,7.5-15.4-5.3-21.4-9.5
c-3.2-2.2-7.7-2.1-11.2-0.8c-3.2,1.2-6.4,0.9-9.8-0.4C-132.9,390.8-136.7,391.9-134.6,403.1z"/>
<line fill="none" stroke="#FFFFFF" stroke-width="3.5" stroke-miterlimit="10" x1="-117.4" y1="421.8" x2="-117.4" y2="390.6"/>
<g opacity="0.75">
<path fill="#FFFFFF" d="M-110.3,389c2.7,0,4.9,0.7,6.8,2.3c5.5,4.6,10.6,6.8,15.6,6.8c1.5,0,2.9-0.2,4.4-0.6
c-0.3,0.9-0.6,1.6-0.7,2.1c-0.4,1.4-1.6,1.8-4.4,2.4c-1.5,0.3-3.1,0.7-4.7,1.5c-1.6,0.8-2.5,2.3-2.5,3.9c0,1.5,0.9,2.8,2.4,3.5
c1.2,0.5,1.5,1.1,1.5,1.4c0,0.3-0.3,0.5-0.5,0.6c-0.6,0.3-1.2,0.4-2.1,0.4c-0.2,0-0.3,0-0.5,0c-0.2,0-0.4,0-0.5,0
c-1.6,0-3.4,0.2-5.2,1.7c-1.5,1.3-2,3.7-1.1,5.6c0.1,0.3,0.1,0.4,0.1,0.4c-0.1,0.1-0.8,0.6-2.6,0.6c-1.8,0-4.1-0.4-6.7-1.2
c-2-0.6-4.5-0.9-7.5-0.9c-3.8,0-8.4,0.6-12.6,1.5c-0.4,0.1-0.9,0.1-1.5,0.1c-1.9,0-4.2-0.5-5.7-1.6c1.7,0,3.3-0.9,4.4-2.4
c1.3-1.8,3.2-6.3,0.2-15.3l-0.1-0.2c-1.3-3.9-2.9-8.8-1.5-10.7c0.5-0.7,1.6-1.1,3.4-1.1c1.2,0,2.6,0.2,4.3,0.5
c1.8,0.4,3.6,0.6,5.4,0.6c1.9,0,3.7-0.2,5.1-0.7C-115.4,389.7-112.9,389-110.3,389 M-110.3,387c-2.7,0-5.2,0.6-7.3,1.3
c-1.3,0.4-2.9,0.6-4.6,0.6c-1.6,0-3.4-0.2-5-0.5c-1.9-0.4-3.4-0.6-4.7-0.6c-8.5,0-5.9,7.7-3.6,14.6c3.4,10.4,0.3,15.1-2.7,15.1
c-1.2,0-2.5-0.8-3.3-2.3c-1.5,5.6,4.8,7.9,9,7.9c0.7,0,1.4-0.1,2-0.2c3.7-0.8,8.1-1.5,12.2-1.5c2.5,0,4.9,0.2,6.9,0.8
c3,0.9,5.5,1.3,7.3,1.3c4,0,5.3-1.8,4.2-3.9c-0.5-1-0.2-2.4,0.6-3.1c1.4-1.2,2.7-1.3,3.9-1.3c0.3,0,0.7,0,1,0c1,0,2-0.1,3.1-0.7
c2.1-1.2,2.1-3.9-1.2-5.5c-1.7-0.8-1.6-2.9,0.2-3.8c3.7-2,8.8-1,10-5.1c0.4-1.5,1.7-5.2,3.3-7.1c-2.9,2-5.9,3-8.9,3
c-4.5,0-9.2-2.2-14.3-6.4C-104.7,387.7-107.6,387-110.3,387L-110.3,387z M-79.1,393.1L-79.1,393.1L-79.1,393.1z"/>
</g>
<path fill="none" stroke="#034672" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M-79.1,393.1c-7.2,5-14.7,3.7-23.2-3.4c-4.9-4.1-11.2-2.8-15.4-1.5c-2.6,0.8-6.3,0.7-9.5,0.1c-14-2.9-10.9,6-8.3,14
c4.8,14.6-3.2,18-6,12.8c-1.8,6.6,7.1,8.6,10.9,7.7c6-1.4,14.1-2.2,19-0.6c9.5,2.9,13.1,0.5,11.5-2.6c-0.5-1-0.2-2.4,0.6-3.1
c2.8-2.4,5.3-0.4,8-1.9c2.1-1.2,2.1-3.9-1.2-5.5c-1.7-0.8-1.6-2.9,0.2-3.8c3.7-2,8.8-1,10-5.1C-81.9,398.7-80.7,395-79.1,393.1z"/>
<path fill="#034672" d="M-134.6,402.1c-2.8-7.5-6.5-16.6,7.5-13.7c3.2,0.7,6.9,0.7,9.5-0.1c4.3-1.3,10-2.4,14.5,1.5
c8.9,7.7,16.9,8.3,24.1,3.3c-1.6,1.9-3.1,5.6-3.3,7.1c-0.2,1.3-1.1,1.9-2.4,2.4c-13.1,4.8-14.1-5.5-20.1-9.7
c-3.2-2.2-9-1.9-12.5-0.6c-3.2,1.2-6.4,0.9-9.8-0.4C-132.9,389.8-136.7,390.9-134.6,402.1z"/>
<path fill="#FFFFFF" d="M-112.4,367.4c-1.1-0.6-4.3-2.6-5.1-3.1c-0.8-0.5-2.7-1.9-4.8-1.9s-4,1.4-4.8,1.9c-0.8,0.5-4,2.5-5.1,3.1
c-0.8,0.5-3.7,2-3.7,6.3c0,1.3,0,3.6,0,4.5c0,2.8,1.6,5,4.5,6.4c1,0.5,1.8,1.3,1.8,3.5c0,0.9,0.4,1.2,0.9,1.2h0.7
c0.6,0,1,0.2,1,1.3c0,0.6,0,2.1,0,2.1v37.2l2.7,2.8h1.2l5.7-5.8v-34.2c0,0,0-1.5,0-2.1c0-1,0.3-1.3,1-1.3h0.7
c0.6,0,0.9-0.2,0.9-1.2c0-2.1,0.8-3,1.8-3.5c2.9-1.5,4.5-3.6,4.5-6.4c0-1,0-3.2,0-4.5C-108.7,369.5-111.6,367.9-112.4,367.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-217.9037" y1="2215.0122" x2="-187.068" y2="2207.345" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#B8D7E9"/>
<stop offset="0.3324" style="stop-color:#CCE3F1"/>
<stop offset="0.6937" style="stop-color:#DAEBF7"/>
<stop offset="1" style="stop-color:#DFEEF9"/>
</linearGradient>
<path fill="url(#SVGID_4_)" d="M-112.4,367.4c-1.1-0.6-4.3-2.6-5.1-3.1c-0.8-0.5-2.7-1.9-4.8-1.9v69.3l4.8-4.9v-34.2
c0,0,0-1.5,0-2.1c0-1,0.3-1.3,1-1.3h0.7c0.6,0,0.9-0.2,0.9-1.2c0-2.1,0.8-3,1.8-3.5c2.9-1.5,4.5-3.6,4.5-6.4c0-1,0-3.2,0-4.5
C-108.7,369.5-111.6,367.9-112.4,367.4z"/>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-214.9444" y1="2190.8406" x2="-206.3191" y2="2233.0925" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#F1F9FE"/>
<stop offset="0.4254" style="stop-color:#E9F5FD"/>
<stop offset="1" style="stop-color:#E4F3FD"/>
</linearGradient>
<path fill="url(#SVGID_5_)" d="M-122.3,362.5c-2.1,0-4,1.4-4.8,1.9c-0.8,0.5-4,2.5-5.1,3.1c-0.8,0.5-3.7,2-3.7,6.3
c0,1.3,0,3.6,0,4.5c0,2.8,1.6,5,4.5,6.4c1,0.5,1.8,1.3,1.8,3.5c0,0.9,0.4,1.2,0.9,1.2h0.7c0.6,0,1,0.2,1,1.3c0,0.6,0,2.1,0,2.1
v37.2l2.7,2.8h1.2l0.9-0.9L-122.3,362.5L-122.3,362.5z"/>
<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="10" d="M-122.6,362.5c1.9,0.9,3.4,2,4.2,2.4
c0.8,0.5,4,2.5,5.1,3.1c0.8,0.5,3.7,2,3.7,6.3c0,1.3,0,2.8,0,3.8c0,2.8-1.6,5-4.5,6.4c-1,0.5-1.8,1.4-1.8,3.6v1.2"/>
<path fill="none" stroke="#034672" stroke-width="2" stroke-miterlimit="10" d="M-112.4,367.4c-1.1-0.6-4.3-2.6-5.1-3.1
c-0.8-0.5-2.7-1.9-4.8-1.9s-4,1.4-4.8,1.9c-0.8,0.5-4,2.5-5.1,3.1c-0.8,0.5-3.7,2-3.7,6.3c0,1.3,0,3.6,0,4.5c0,2.8,1.6,5,4.5,6.4
c1,0.5,1.8,1.3,1.8,3.5c0,0.9,0.4,1.2,0.9,1.2h0.7c0.6,0,1,0.2,1,1.3c0,0.6,0,2.1,0,2.1v37.2l2.7,2.8h1.2l5.7-5.8v-34.2
c0,0,0-1.5,0-2.1c0-1,0.3-1.3,1-1.3h0.7c0.6,0,0.9-0.2,0.9-1.2c0-2.1,0.8-3,1.8-3.5c2.9-1.5,4.5-3.6,4.5-6.4c0-1,0-3.2,0-4.5
C-108.7,369.5-111.6,367.9-112.4,367.4z"/>
<path fill="#E9F6FE" stroke="#034672" stroke-width="2" stroke-miterlimit="10" d="M-107.5,392.7"/>
<g>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-106.4885" y1="2203.667" x2="-106.4885" y2="2254.552" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_6_)" d="M-14.5,407.6h-11.6c0.3,2.3,1,3.9,2.1,4.7s2.9,1.2,5.5,1.2c3.4,0,7.4-0.2,12-0.7l0.9,6
c-3.3,1.6-8,2.4-14,2.4c-6,0-10.3-1.3-12.9-3.9c-2.6-2.6-3.8-6.9-3.8-12.9c0-6.3,1.2-10.7,3.7-13.2c2.5-2.5,6.7-3.8,12.6-3.8
c5.3,0,9.2,0.9,11.6,2.8c2.4,1.8,3.6,4.7,3.6,8.4c0,3-0.8,5.2-2.3,6.7C-8.5,406.8-11,407.6-14.5,407.6z M-26.3,401.8h9.1
c1.2,0,2-0.3,2.4-0.8c0.4-0.6,0.6-1.4,0.6-2.5c0-1.5-0.4-2.6-1.2-3.2c-0.8-0.6-2.2-0.9-4.3-0.9c-2.5,0-4.2,0.5-5.1,1.5
C-25.6,396.9-26.1,398.8-26.3,401.8z"/>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="-72.864" y1="2203.667" x2="-72.864" y2="2254.5503" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_7_)" d="M31,388.3l-11.4,32.6c-0.6,1.7-1.4,3.3-2.2,4.8c-0.9,1.5-2,2.9-3.4,4.3c-1.4,1.4-3.1,2.4-5.2,3
c-2.1,0.7-4.4,0.8-6.9,0.5L1,427.8c2.6-0.7,4.6-1.6,6.1-2.7c1.5-1.1,2.6-2.6,3.5-4.5l0.1-0.3H8.9c-1.6,0-2.6-0.8-3.1-2.3
l-10.5-29.8H5.6l6,20.8c0.4,1.5,0.7,3.1,1,4.8h0.8c0.1-0.5,0.3-1.3,0.7-2.4s0.5-1.9,0.7-2.4l5.9-20.8L31,388.3L31,388.3z"/>
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="-41.775" y1="2203.667" x2="-41.775" y2="2254.552" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_8_)" d="M57.6,412.8l0.9,6.3c-3.3,1.4-7.5,2.1-12.4,2.1c-5.9,0-10.1-1.3-12.5-3.8
c-2.4-2.5-3.6-6.9-3.6-13.1c0-6.2,1.2-10.6,3.6-13.1c2.4-2.5,6.6-3.8,12.6-3.8c4.9,0,8.8,0.7,11.9,2l-1,6
c-4.7-0.1-7.8-0.1-9.2-0.1c-3,0-5,0.6-6.1,1.9c-1.1,1.3-1.7,3.6-1.7,7.2c0,3.5,0.6,5.9,1.7,7.2c1.1,1.3,3.1,1.9,6.1,1.9
C51.6,413.4,54.9,413.2,57.6,412.8z"/>
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="8.9715" y1="2203.667" x2="8.9715" y2="2254.553" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_9_)" d="M95,387.4c6,0,10.2,1.3,12.7,3.9c2.5,2.6,3.7,7,3.7,13.1c0,6.2-1.2,10.5-3.7,13.1
c-2.5,2.5-6.7,3.8-12.7,3.8c-6,0-10.3-1.3-12.8-3.8c-2.5-2.5-3.7-6.9-3.7-13.1c0-6.2,1.2-10.6,3.7-13.2
C84.7,388.7,89,387.4,95,387.4z M95,394.6c-2.6,0-4.3,0.6-5.1,2c-0.9,1.3-1.3,3.9-1.3,7.8c0,3.8,0.4,6.4,1.3,7.7
c0.9,1.3,2.6,2,5.1,2c2.5,0,4.2-0.7,5.1-2c0.9-1.3,1.3-3.9,1.3-7.7c0-3.9-0.4-6.5-1.3-7.8C99.2,395.3,97.5,394.6,95,394.6z"/>
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="42.879" y1="2203.667" x2="42.879" y2="2254.552" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_10_)" d="M115.9,395.5l-0.9-6.5c5.8-1,11.4-1.6,16.7-1.6c4.3,0,7.5,0.9,9.6,2.6c2,1.8,3.1,4.8,3.1,9.2v21.2
h-7.6l-1-4.9c-3.2,3.8-7.2,5.7-11.9,5.7c-3.1,0-5.6-0.8-7.6-2.4s-2.9-3.9-2.9-6.8v-3.3c0-2.6,0.9-4.6,2.6-6
c1.7-1.4,4.1-2.1,7.2-2.1h11.5v-1.4c0-1.6-0.4-2.8-1.1-3.3c-0.7-0.6-2.1-0.8-4.1-0.8C125.9,394.9,121.4,395.1,115.9,395.5z
M122.9,409.4v1.2c0,2.2,1.3,3.3,3.8,3.3c2.6,0,5.3-1,8-3.1v-4.5H126C124,406.4,122.9,407.4,122.9,409.4z"/>
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="-136.063" y1="2203.667" x2="-136.063" y2="2254.5491" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_11_)" d="M-43.5,406.1c-0.9-1.6-1.9-2.7-3-3.5v-0.1c1.3-0.9,2.3-1.9,3-3.1l8.3-11.1H-46l-7.9,11.2h-3
c0.3-1.8,0.5-3.7,0.5-5.9v-18.8h-3l-6.7,9.5v36.1h9.6v-10.1c0-1.3-0.2-2.9-0.5-4.8h3.1l9,14.9H-34L-43.5,406.1z"/>
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="-16.68" y1="2203.667" x2="-16.68" y2="2254.551" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_12_)" d="M76.7,413.2h-2.6c-2.4,0-3.6-1.1-3.6-3.3v-35.1h-3l-6.6,9.4v28.2c0,2.8,0.8,5,2.5,6.5
c1.6,1.5,3.9,2.3,6.8,2.3c3.5,0,6-0.4,7.7-1.3L76.7,413.2z"/>
<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="78.107" y1="2203.667" x2="78.107" y2="2254.5491" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#FFFFFE"/>
<stop offset="0.4815" style="stop-color:#F2F1F1"/>
<stop offset="1" style="stop-color:#E9E7E8"/>
</linearGradient>
<path fill="url(#SVGID_13_)" d="M170.7,406.1c-0.9-1.6-1.9-2.7-3-3.5v-0.1c1.3-0.9,2.3-1.9,3-3.1l8.3-11.1h-10.8l-7.9,11.2h-3
c0.3-1.8,0.5-3.7,0.5-5.9v-18.8h-3l-6.7,9.5v36.1h9.6v-10.1c0-1.3-0.2-2.9-0.5-4.8h3.1l9,14.9h10.8L170.7,406.1z"/>
</g>
<linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="-208.281" y1="2186.001" x2="-208.281" y2="2196.1689" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#00639A"/>
<stop offset="0.2355" style="stop-color:#00578A"/>
<stop offset="0.507" style="stop-color:#004D7B"/>
<stop offset="0.7167" style="stop-color:#004773"/>
<stop offset="1" style="stop-color:#00446E"/>
</linearGradient>
<polygon fill="url(#SVGID_14_)" points="-122.3,372.4 -131.3,370.4 -132.3,374.1 -122.3,379.2 -112.2,374.1 -113.2,370.4 "/>
<polyline opacity="0.2" enable-background="new " points="-122.3,379.2 -112.2,374.1 -113.2,370.4 -122.3,372.4 "/>
<path fill="#034672" d="M-127.2,391.9c3.4,1.3,6.7,1.5,9.8,0.4l-0.3-2.2c-2.6,0.8-6.3,0.7-9.5,0.1L-127.2,391.9z"/>
<path fill="#034672" d="M-122.6,391.5c-0.4,2.8,1.9,4.7,1.9,7.6C-116.8,394.8-121.7,393.3-122.6,391.5z"/>
<path fill="#034672" d="M-122.6,391.7c-0.1,3.2-0.4,5.6-1.7,7.2C-126.8,394.2-123.7,392.6-122.6,391.7z"/>
<circle fill="#034672" cx="-122.6" cy="391.7" r="1.6"/>
<linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="-197.511" y1="2237.772" x2="-197.511" y2="2237.772" gradientTransform="matrix(1 0 0 1 86 -1815.5)">
<stop offset="0" style="stop-color:#F6F6F6"/>
<stop offset="0.5386" style="stop-color:#E4E4E4"/>
<stop offset="1" style="stop-color:#DADADA"/>
</linearGradient>
<path fill="url(#SVGID_15_)" d="M-111.5,422.3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

5
src/admin/style.scss Normal file
View File

@@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap');
body {
font-family: 'IBM Plex Sans', sans-serif;
}

View File

@@ -0,0 +1,4 @@
parent=base
import=common/keycloak
styles=lib/patternfly/css/patternfly.css node_modules/select2/select2.css css/styles.css lib/angular/treeview/css/angular.treeview.css node_modules/text-security/dist/text-security.css
scripts=js/bundle.js

1
src/login/index.ts Normal file
View File

@@ -0,0 +1 @@
import './style.scss';

97
src/login/login.ftl Normal file
View File

@@ -0,0 +1,97 @@
<#import "template.ftl" as layout>
<@layout.registrationLayout displayInfo=social.displayInfo displayWide=(realm.password && social.providers??); section>
<#if section = "header">
${msg("doLogIn", client.name)}
<#elseif section = "form">
<div id="kc-form" <#if realm.password && social.providers??>class="${properties.kcContentWrapperClass!}"</#if>>
<div id="kc-form-wrapper"
<#if realm.password && social.providers??>class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}"</#if>>
<#if realm.password>
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}"
method="post">
<div class="${properties.kcFormGroupClass!}">
<label for="username"
class="${properties.kcLabelClass!}">
<#if !realm.loginWithEmailAllowed>
${msg("username")}
<#elseif !realm.registrationEmailAsUsername>
${msg("usernameOrEmail")}
<#else>
${msg("email")}
</#if>
</label>
<#if usernameEditDisabled??>
<input tabindex="1" id="username" class="${properties.kcInputClass!}" name="username"
value="${(login.username!'')}" type="text" disabled
placeholder="<#if !realm.loginWithEmailAllowed>${msg("username")} <#elseif !realm.registrationEmailAsUsername> ${msg("usernameOrEmail")} <#else> ${msg("email")} </#if>"/>
<#else>
<input tabindex="1" id="username" class="${properties.kcInputClass!}" name="username"
value="${(login.username!'')}" type="text" autofocus
placeholder="<#if !realm.loginWithEmailAllowed>${msg("username")} <#elseif !realm.registrationEmailAsUsername> ${msg("usernameOrEmail")} <#else> ${msg("email")} </#if>" autocomplete="off"/>
</#if>
</div>
<div class="${properties.kcFormGroupClass!}">
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
type="password" autocomplete="off" placeholder="${ msg("password") }"/>
</div>
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
<div id="kc-form-options">
<#if realm.rememberMe && !usernameEditDisabled??>
<div class="bx--form-item">
<#if login.rememberMe??>
<input tabindex="3" class="bx--checkbox" id="rememberMe" name="rememberMe"
type="checkbox" checked>
<#else>
<input tabindex="3" class="bx--checkbox" id="rememberMe" name="rememberMe"
type="checkbox">
</#if>
<label class="bx--checkbox-label" for="rememberMe">
${msg("rememberMe")}
</label>
</div>
</#if>
</div>
<div class="${properties.kcFormOptionsWrapperClass!}">
<#if realm.resetPasswordAllowed>
<span><a tabindex="5"
href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
</#if>
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
<input tabindex="4"
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</form>
</#if>
</div>
<#if realm.password && social.providers??>
<div id="kc-social-providers"
class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}">
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 4>${properties.kcFormSocialAccountDoubleListClass!}</#if>">
<#list social.providers as p>
<li class="${properties.kcFormSocialAccountListLinkClass!}"><a href="${p.loginUrl}"
id="zocial-${p.alias}"
class="zocial ${p.providerId}">
<span>${p.displayName}</span></a></li>
</#list>
</ul>
</div>
</#if>
</div>
<#elseif section = "info" >
<#if realm.password && realm.registrationAllowed && !usernameEditDisabled??>
<div id="kc-registration">
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
</div>
</#if>
</#if>
</@layout.registrationLayout>

43
src/login/style.scss Normal file
View File

@@ -0,0 +1,43 @@
$app-page-background: #ECECEC;
$app-toolbar-background: #FFF;
@import "~carbon-components/css/carbon-components.css";
@import "style/login";
html {
font-size: 20px;
}
body {
padding: 0;
margin: 0;
}
.SideBar {
border-right: 1px #EEE solid;
}
.PageTitle {
text-align: center;
font-size: 1.4rem;
margin-top: 2rem;
}
.FixedNotification {
position: fixed;
bottom: 0;
left: 12px;
z-index: 999999;
}
.FixedLanguageSelect {
display: flex;
position: fixed;
bottom: 0;
padding: 1rem;
background-color: #FFF;
width: 100%;
.FillFlex {
flex-grow: 1;
}
}

View File

@@ -0,0 +1,73 @@
@media (max-width: 66rem) {
.login-pf-page.bx--content {
padding: 0;
.bx--tile {
background-color: transparent;
}
}
}
.login-pf {
.bx--tile {
background-color: #FFF;
}
}
body, .bx--content {
background-color: #f3f3f3;
@media (max-width: 66rem) {
background-color: transparent;
.FixedLanguageSelect {
.bx--select, select {
width: 100%;
}
}
}
}
.login-pf-page.bx--content {
.bx--form-item {
margin-bottom: .5rem;
}
.bx--tile {
max-width: 30rem;
margin: 0 auto;
background-color: #FFF;
padding: 1.1rem;
&.bx--wide {
max-width: none;
}
}
.login-pf-settings {
> * {
margin-bottom: .4rem;
}
}
}
.page-title {
font-weight: 400;
font-size: 1.5rem;
line-height: 36px;
letter-spacing: 0;
margin-bottom: 1rem;
}
#kc-form-buttons {
margin-top: .4rem;
}
.DisplayFlex {
display: flex;
.FillFlex {
flex-grow: 1;
}
}

109
src/login/template.ftl Normal file
View File

@@ -0,0 +1,109 @@
<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayWide=false>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" class="${properties.kcHtmlClass!}">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="robots" content="noindex, nofollow">
<#if properties.meta?has_content>
<#list properties.meta?split(' ') as meta>
<meta name="${meta?split('==')[0]}" content="${meta?split('==')[1]}"/>
</#list>
</#if>
<title>${msg("loginTitle", (realm.displayName!''))}</title>
<link rel="icon" href="${url.resourcesPath}/img/favicon.ico"/>
<#if properties.styles?has_content>
<#list properties.styles?split(' ') as style>
<link href="${url.resourcesPath}/${style}" rel="stylesheet"/>
</#list>
</#if>
<#if properties.scripts?has_content>
<#list properties.scripts?split(' ') as script>
<script src="${url.resourcesPath}/${script}" type="text/javascript"></script>
</#list>
</#if>
<#if scripts??>
<#list scripts as script>
<script src="${script}" type="text/javascript"></script>
</#list>
</#if>
</head>
<body class="${properties.kcBodyClass!}">
<header aria-label="Header" class="bx--header bx--header--website" role="banner">
<a class="bx--skip-to-content"
href="#main-content"
tabindex="0">
Skip to main content
</a>
<a class="bx--header__name" href="/">
${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}
</a>
</header>
<div class="${properties.kcLoginClass!} bx--content" id="main-content">
<div class="${properties.kcFormCardClass!} form-card bx--tile <#if displayWide>${properties.kcFormCardAccountClass!} bx--wide</#if>">
<header class="${properties.kcFormHeaderClass!}">
<h1 id="kc-page-title" class="page-title"><#nested "header"></h1>
</header>
<div id="kc-content">
<div id="kc-content-wrapper">
<#if displayMessage && message?has_content>
<div class="alert alert-${message.type}">
<#if message.type = 'success'><span
class="${properties.kcFeedbackSuccessIcon!}"></span></#if>
<#if message.type = 'warning'><span
class="${properties.kcFeedbackWarningIcon!}"></span></#if>
<#if message.type = 'error'><span class="${properties.kcFeedbackErrorIcon!}"></span></#if>
<#if message.type = 'info'><span class="${properties.kcFeedbackInfoIcon!}"></span></#if>
<span class="kc-feedback-text">${kcSanitize(message.summary)?no_esc}</span>
</div>
</#if>
<#nested "form">
<#if displayInfo>
<div id="kc-info" class="${properties.kcSignUpClass!}">
<div id="kc-info-wrapper" class="${properties.kcInfoAreaWrapperClass!}">
<#nested "info">
</div>
</div>
</#if>
</div>
</div>
</div>
</div>
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
<div class="FixedLanguageSelect">
<span class="FillFlex"></span>
<div class="bx--select">
<label for="select-id" class="bx--label">Select language</label>
<div class="bx--select-input__wrapper">
<select id="select-id" class="bx--select-input" onchange="window.location = this.value" >
<#list locale.supported as l>
<option class="bx--select-option"value="${ l.url }" <#if locale.current == l.label>selected</#if>>
${l.label}
</option>
</#list>
</select>
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;"
xmlns="http://www.w3.org/2000/svg" class="bx--select__arrow" width="10" height="6"
viewBox="0 0 10 6" aria-hidden="true">
<path d="M5 6L0 1 .7.3 5 4.6 9.3.3l.7.7z"></path>
</svg>
</div>
</div>
</div>
</#if>
</body>
</html>
</#macro>

View File

@@ -0,0 +1,69 @@
parent=base
import=common/keycloak
styles=
scripts=js/bundle.js
meta=viewport==width=device-width,initial-scale=1
kcHtmlClass=login-pf
kcLoginClass=login-pf-page
kcLogoLink=http://www.keycloak.org
kcLogoClass=login-pf-brand
kcContainerClass=container-fluid
kcContentClass=col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3
kcContentWrapperClass=row
kcHeaderClass=login-pf-page-header
kcFeedbackAreaClass=col-md-12
kcLocaleClass=col-xs-12 col-sm-1
kcAlertIconClasserror=pficon pficon-error-circle-o
kcFormAreaClass=col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2
kcFormCardClass=card-pf
kcFormCardAccountClass=login-pf-accounts
kcFormSocialAccountClass=login-pf-social-section
kcFormSocialAccountContentClass=col-xs-12 col-sm-6
kcFormSocialAccountListClass=login-pf-social list-unstyled login-pf-social-all
kcFormSocialAccountDoubleListClass=login-pf-social-double-col
kcFormSocialAccountListLinkClass=login-pf-social-link
kcFormHeaderClass=login-pf-header
kcFeedbackErrorIcon=pficon pficon-error-circle-o
kcFeedbackWarningIcon=pficon pficon-warning-triangle-o
kcFeedbackSuccessIcon=pficon pficon-ok
kcFeedbackInfoIcon=pficon pficon-info
doLogIn=Sign in to continue to {}
kcFormClass=form-horizontal
kcFormGroupClass=bx--form-item
kcFormGroupErrorClass=has-error
kcLabelClass=bx--label
kcLabelWrapperClass=
kcInputClass=bx--text-input
kcInputWrapperClass=
kcFormOptionsClass=
kcFormButtonsClass=
kcFormSettingClass=login-pf-settings
kcTextareaClass=form-control
kcSignUpClass=login-pf-signup
kcInfoAreaClass=col-xs-12 col-sm-4 col-md-4 col-lg-5 details
##### css classes for form buttons
# main class used for all buttons
kcButtonClass=bx--btn
# classes defining priority of the button - primary or default (there is typically only one priority button for the form)
kcButtonPrimaryClass=bx--btn--primary
kcButtonDefaultClass=
# classes defining size of the button
kcButtonLargeClass=btn-lg
kcButtonBlockClass=btn-block
##### css classes for input
kcInputLargeClass=input-lg
##### css classes for form accessability
kcSrOnlyClass=sr-only

26
src/welcome/index.ftl Normal file
View File

@@ -0,0 +1,26 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Welcome to ${productNameFull}</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="robots" content="noindex, nofollow">
<link rel="shortcut icon" href="welcome-content/favicon.ico" type="image/x-icon">
<#if properties.styles?has_content>
<#list properties.styles?split(' ') as style>
<link href="${resourcesPath}/${style}" rel="stylesheet" />
</#list>
</#if>
</head>
<body>
<div class="container-fluid">
<div class="row">
</div>
</div>
</body>
</html>

0
src/welcome/index.ts Normal file
View File

4
src/welcome/style.scss Normal file
View File

@@ -0,0 +1,4 @@
$app-page-background: #ECECEC;
$app-toolbar-background: #FFF;
@import "~carbon-components/css/carbon-components.css";

View File

@@ -0,0 +1,6 @@
import=common/keycloak
styles=node_modules/patternfly/dist/css/patternfly.css node_modules/patternfly/dist/css/patternfly-additions.css css/welcome.css
documentationUrl=https://www.keycloak.org/documentation.html
displayCommunityLinks=true

20
tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"noImplicitAny": false,
"module": "es6",
"target": "es5",
"allowJs": true,
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowSyntheticDefaultImports": true
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
}

103
webpack.config.dev.js Normal file
View File

@@ -0,0 +1,103 @@
const webpack = require('webpack');
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const commonConfig = (entryPoint) => ({
entry: `./src/${entryPoint}/index.ts`,
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
module: {
rules: [
{
include: [
path.resolve(__dirname, 'src', entryPoint, '*'),
],
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
options: {
plugins: [
'syntax-dynamic-import',
],
presets: [
[
'@babel/preset-env',
{
modules: false,
},
],
],
},
test: /\.js$/,
},
{
test: /\.(scss|css)$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
},
],
},
],
},
output: {
filename: 'bundle.js',
path: path.join(__dirname, `./carbon/${entryPoint}/resources/js/`),
},
mode: process.env.NODE_ENV || 'development',
plugins: [
new CleanWebpackPlugin(),
new webpack.BannerPlugin({
banner: () => `Copyright ${new Date().getFullYear()}`,
}),
new webpack.EnvironmentPlugin({}),
new Dotenv(),
new CopyPlugin([
{
from: path.join(__dirname, `src/${entryPoint}`),
to: path.join(__dirname, `/carbon/${entryPoint}`),
ignore: ['*.js', '*.ts', '*.scss'],
},
]),
],
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/,
},
},
chunks: 'async',
minChunks: 1,
minSize: 30000,
name: true,
},
minimizer: [],
},
});
module.exports = ['login', 'account', 'welcome', 'admin'].map(res => commonConfig(res));

10
webpack.config.js Normal file
View File

@@ -0,0 +1,10 @@
const devConfig = require('./webpack.config.dev');
const TerserPlugin = require('terser-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
for (const config of devConfig) {
config.optimization.minimizer.push(new TerserPlugin());
config.plugins.push(new UglifyJSPlugin());
}
app.exports = devConfig;