Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • applications/etuc/hubl
  • applications/hubl
  • decentral1se/hubl
  • rngadam/hubl
  • jvtrudel/hubl
  • 3wc/hubl
6 results
Show changes
Showing
with 730 additions and 111 deletions
#members>div:first-of-type {
min-height: calc(100vh - 50px);
}
#admin-projects-create .form solid-multiple-form {
height: 100%;
button {
border: 1px solid var(--color-secondary);
text-transform: uppercase;
color: var(--color-secondary);
background-color: white;
font-weight: 700;
padding: 9px 20px;
font-size: 12px;
line-height: 14px;
border-radius: 16.5px;
@media (max-width: 768px) {
width: 100%;
}
}
button:hover {
background-color: var(--color-secondary);
color: white;
}
}
#projects-edit .edit-businessprovider form {
display: table-row;
border-bottom: 1px solid #C9C8C8;
border-right: 1px solid #C9C8C8;
text-align: center;
.segment.table-cell {
border-bottom: 1px solid #C9C8C8;
border-right: 1px solid #C9C8C8;
height: 60px;
padding: 10px;
}
[type="submit"] {
margin: 19px auto;
}
}
#projects-information .display-businessprovider {
[name="name"]::after {
content: " - ";
margin-left: 5px;
}
[name="fee"]:after {
content: " %";
}
> div > solid-display:not(:first-child) [name="titlebusinessprovider"] {
display: none;
}
}
.index-community {
position: absolute;
top: 0;
left: 0;
height: 100%;
> div {
min-height: 75%;
margin-top: 100px;
padding-bottom: 70px;
@media (max-width: 768px) {
margin: 0;
min-height: 100%;
}
.community-logo {
max-width: 100%;
height: 100px;
margin-top: 70px;
display: flex;
align-items: center;
justify-content: center;
}
.community-button {
height: 80px;
border-radius: 5px;
}
.community-button-flexed {
flex-basis: 33%;
flex-grow: 1;
flex-shrink: 1;
}
.community-button-flexed-large {
flex-basis: 66%;
flex-grow: 1;
flex-shrink: 1;
}
.community-flex-container {
display: flex;
flex-wrap: wrap;
text-align: center;
justify-content: center;
> div > solid-display {
display: contents;
> div > orbit-index-select-community {
display: contents;
}
}
}
.community-button-flex-container {
display: flex;
align-items: center;
justify-content: center;
> solid-display,
orbit-index-community-logo,
orbit-index-community-text {
display: contents;
}
}
}
solid-form-text-label,
solid-form-password-label,
orbit-index-input-type-email {
color: #5d7393;
}
label {
text-align: left;
}
[type="submit"] {
float: initial !important;
}
}
include hd-user-avatar.pug
sib-widget(name='circle-team-template')
template
sib-display(
data-src="${await value}"
fields='account.picture, sup(name, groups), sub(profile.city)'
widget-account.picture='hd-user-avatar'
widget-groups='hd-user-groups'
multiple-groups=''
)
sib-widget(name='hd-user-groups')
template ${await value.name}
sib-widget(name='hd-user-avatar')
template ${await value ? `<img src="${await value}" style="max-width:100%; max-height: 100%;" />` : `<object type="image/svg+xml" data="/images/alien.svg"></object>`}
sib-widget(name='template-business-provider')
template
ul
li #[span Happy Dev Paris:] 5%
li #[span Business provider:] ${value.name}, ${value.fee ? value.fee:0}%
\ No newline at end of file
include hd-user-avatar.pug
sib-widget(name='captain-template')
template
sib-display.project-profile-user-avatar(
data-src="${value ? value['@id'] : ''}"
fields='account.picture',
widget-account.picture='hd-user-avatar'
)
div(name='user.thumb')
sib-display(
data-src="${value ? value['@id'] : ''}"
fields='name'
)
sib-display(
data-src="${value ? value['@id'] : ''}",
fields='name'
nested-field="groups"
)
sib-display(
data-src="${value ? value['@id'] : ''}"
fields='username'
)
sib-widget(name='customer-template')
template
div#clientBox
div
h5 Client:
ul
li #[span Business name:]${value.name}
li #[span Company register:]${value.companyRegister}
li
span Address:
br
p ${value.address}
div
h5 Contact:
ul
li(class='mdi-account-outline') #[span ${value.firstName} ${value.lastName}], ${value.role}
li(class='mdi-email-outline')
a(href='mailto:${value.email}') ${value.email}
li(class='mdi-cellphone-iphone') ${value.phone}
sib-widget(name='groups-name')
template ${value.name}
sib-widget(name='joboffers-filter')
template
select
option(name='Offers',disabled) Offers
option(selected,name='Current offers') Current offers
option(name='Expired offers') Expired offers
option(name='All offers') All offers
option(name='My offers') Only my offers
\ No newline at end of file
sib-widget(name='skills-name')
template
li(class='skill') ${value.name}
sib-widget(name='status-template')
template
div#member-status ${value == true ? '<span class="status-one">Available</span>' : '<span class="status-two">Busy</span>'}
include hd-user-avatar.pug
sib-widget(name='project-team-template')
template
sib-display.project-profile-user-avatar(
data-src="${value.user ? value.user['@id'] : ''}"
fields='account.picture',
widget-account.picture='hd-user-avatar'
)
div(name='user.thumb')
sib-display(
data-src="${value.user ? value.user['@id'] : ''}"
fields='name'
)
sib-display(
data-src="${value.user ? value.user['@id'] : ''}",
fields='name'
nested-field="groups"
)
sib-display(
data-src="${value.user ? value.user['@id'] : ''}"
fields='username'
)
import { resolve } from "node:path";
import { createHtmlPlugin } from 'vite-plugin-html'
import { defineConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import Handlebars from "handlebars";
import handlebars from "vite-plugin-handlebars";
import config from "./vite/generateConfig.mjs";
// Workaround for https://github.com/alexlafroscia/vite-plugin-handlebars/issues/192
function handlebarsOverride(options) {
const plugin = handlebars(options);
plugin.handleHotUpdate = async ({ server, file }) => {
if (file.endsWith(".html") || file.endsWith(".hbs"))
server.ws.send({
type: "full-reload",
});
};
return plugin;
}
export default defineConfig({
build: {
rollupOptions: {
input: {
app: resolve(__dirname, "index.html"),
},
},
},
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
api: "modern-compiler"
},
},
},
define: config,
plugins: [
handlebarsOverride({
helpers: {
json: (value) => JSON.stringify(value),
year: () => new Date().getFullYear(),
isArray: (value) => Array.isArray(value),
mergeAttributes: (attributes) => {
let result = "";
for (const key in attributes) {
if (attributes.hasOwnProperty(key) && attributes[key] !== null) {
result += ` ${key}="${attributes[key]}"`;
}
}
return new Handlebars.SafeString(result);
},
hasComponent: (...component) =>
component.slice(0, -1).some((e) => config.componentSet.includes(e)),
hasComponentAll: (...component) =>
component.slice(0, -1).every((e) => config.componentSet.includes(e)),
hasNpmPackage: (...packages) =>
packages.slice(0, -1).every((e) => config.npm.filter(n => n.package === e).length > 0),
getComponent: (component) =>
config.components.find((c) => c.type === component),
getComponentFromRoute: (route) =>
config.components.find((c) => c.route === route),
getDefaultRoute: config.helpers.getDefaultRoute,
getRoute: config.helpers.getRoute,
get_legacy_view: (...name) => `legacy/${name.slice(0, -1).join('')}`,
is: (cond1, cond2) => cond1 === cond2,
any_are: (value, ...cond) => cond.slice(0, -1)?.includes(value),
includes: (arr, cond) => arr?.includes(cond),
},
partialDirectory: [resolve(__dirname, "src/partials")],
context: config,
}),
VitePWA({
devOptions: {
enabled: false,
},
injectRegister: "auto",
sourcemap: true,
manifest: {
lang: config.client.i18n.lang,
name: config.client.name,
short_name: config.client.shortName || config.client.name,
...config.client.pwa,
},
workbox: {
maximumFileSizeToCacheInBytes: 5000000,
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
handler: "CacheFirst",
options: {
cacheName: "google-fonts-cache",
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365,
},
cacheableResponse: {
statuses: [0, 200],
},
},
},
{
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
handler: "CacheFirst",
options: {
cacheName: "gstatic-fonts-cache",
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365,
},
cacheableResponse: {
statuses: [0, 200],
},
},
},
],
},
}),
createHtmlPlugin({
minify: true,
}),
],
resolve: {
alias: {
"@helpers": "/src/helpers",
"@partials": "/src/partials",
"@styles": "/src/styles",
},
},
});
{
"client": {
"name": "Sample of a functional Orbit",
"description": "",
"logo": "https://cdn.startinblox.com/logos/webp/startinblox.webp",
"server": "http://localhost:8000",
"favicon": "/pwa/favicon.ico",
"css": false,
"defaultAvatar": "/images/alien.webp",
"i18n": {
"lang": "en",
"force": false
},
"pwa": {
"dir": "ltr",
"icons": [
{
"src": "/pwa/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/pwa-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/pwa/pwa-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#fff",
"theme_color": "#FFFFFF"
}
},
"components": [
{
"type": "routing",
"route": false
},
{
"type": "menu",
"route": false
},
{
"type": "menu-top",
"parameters": {
"isBeta": false
},
"route": false
}
],
"npm": []
}
{
"@startinblox/core": {
"version": "0.19",
"path": "https://cdn.jsdelivr.net/npm/@startinblox/core@0.19/dist/index.js"
},
"@startinblox/router": {
"version": "0.12",
"path": "https://cdn.jsdelivr.net/npm/@startinblox/router@0.12/+esm"
},
"@startinblox/oidc": {
"version": "0.16",
"path": "https://cdn.jsdelivr.net/npm/@startinblox/oidc@0.16/+esm",
"requiredBy": ["autoLogin", "registering"]
},
"@startinblox/component-chat": {
"version": "7.1.5",
"requiredBy": ["chat", "circles", "projects", "spaces"]
},
"@startinblox/component-communities": {
"version": "2.0.4",
"requiredBy": ["communities"]
},
"@startinblox/component-dashboard": {
"requiredBy": ["dashboard"]
},
"@startinblox/component-directory": {
"version": "8.0.2",
"requiredBy": ["directory"]
},
"@startinblox/component-event": {
"version": "5.0.7",
"requiredBy": ["events"]
},
"@startinblox/component-conversation": {
"version": "1.0.2",
"requiredBy": ["events", "polls", "resources"]
},
"@startinblox/component-invoicing": {
"requiredBy": ["invoices"]
},
"@startinblox/component-job-board": {
"requiredBy": ["job-board"]
},
"@startinblox/component-notifications": {
"version": "1.0.2",
"requiredBy": ["notification"]
},
"@startinblox/component-poll": {
"requiredBy": ["polls"]
},
"@startinblox/component-spaces": {
"requiredBy": ["spaces"]
},
"@startinblox/component-sales": {
"requiredBy": ["spaces"]
},
"@startinblox/component-resource": {
"version": "5.0.5",
"requiredBy": ["resources"]
},
"@startinblox/component-babelfish": {
"requiredBy": ["babelfish"]
},
"@startinblox/ontochain-directory": {
"requiredBy": ["ontochain-directory"]
},
"@startinblox/component-piswap": {
"requiredBy": ["piswap"]
},
"@startinblox/component-tamis": {
"requiredBy": [
"tamis-profile",
"tamis-commande",
"tamis-prestation",
"tamis-asset"
]
},
"@startinblox/components-tems": {
"requiredBy": ["tems-catalog"]
},
"moralis": {
"path": "https://unpkg.com/moralis/dist/moralis.js",
"requiredBy": ["piswap"]
}
}
import { defaultComposer } from "default-composer";
import { readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import defaultNpm from "./default.npm.json";
import defaultConfig from "./default.config.json";
import convertStringToBoolean from "../src/helpers/convertStringToBoolean.js";
import generateUniq from "../src/helpers/generateUniq.js";
import generateUrl from "../src/helpers/generateUrl.js";
import rewriteServer from "../src/helpers/rewriteServer.js";
/*
Generate the client config from a combination of client.json, default.client.json, default.npm.json
Provide local federations and collision-free routes to each component.
*/
let userConfig;
try {
userConfig = (await import("../config.json")).default;
} catch {
userConfig = {};
}
try {
userConfig.context = (await import("../src/context.json")).default;
} catch {
userConfig.context = {};
}
const config = defaultComposer(defaultConfig, userConfig);
const componentSet = new Set(
[...defaultConfig.components, ...config.components].map((c) => c.type),
);
config.components.map((c) => {
if (c.extensions) {
for (const e of c.extensions) {
componentSet.add(e.type);
}
}
});
const defaultPackage = {
package: "",
version: "latest",
path: false,
};
// Those attributes will not be serialized on a component
const ignoredAttributes = ["replacement", "menu"];
const requiredPackages = Object.keys(defaultNpm)
.map((mod) => {
const replaced = config.npm.findIndex((e) => e.package === mod);
if (replaced === -1) {
const p = defaultNpm[mod];
if (!p.requiredBy || p.requiredBy?.some((dep) => componentSet.has(dep))) {
return defaultComposer(defaultPackage, {
package: mod,
version: p.version,
// path: p.path || `https://cdn.jsdelivr.net/npm/${mod}@${p.version || "latest"}`,
path: p.path,
});
}
} else {
return config.npm.splice(replaced, 1)[0];
}
return false;
})
.filter((p) => p);
const routes = new Set();
const federations = {};
const components = config.components.map((component) => {
if (typeof component.route === "undefined") {
component.route = component.type;
}
component.uniq = generateUniq();
if (component.route) {
let route = component.route;
if (routes.has(component.route)) {
route += `-${component.uniq}`;
}
routes.add(route);
component.route = route;
}
if (component.extensions) {
for (const extension of component.extensions) {
if (typeof extension.route === "undefined") {
extension.route = extension.type;
}
extension.uniq = generateUniq();
if (extension.route) {
let route = extension.route;
if (routes.has(extension.route)) {
route += `-${extension.uniq}`;
}
routes.add(route);
extension.route = route;
}
}
}
if (component.parameters) {
const federation = new Set();
if (config.client.server) {
federation.add(config.client.server);
}
if (config.client.servers) {
for (const server of config.client.servers) {
federation.add(server);
}
}
if (component.federation) {
for (const target of component.federation) {
federation.add(target);
}
}
component.federation = [...federation];
for (const [attribute, path] of Object.entries(component.parameters)) {
if (typeof path === "string") {
if (path.startsWith("federation://")) {
const contains = generateUrl(federation, path);
if (contains.length > 1) {
federations[`store://local.${component.uniq}/${attribute}/`] = {
"@cache": "false",
"@context": "https://cdn.startinblox.com/owl/context.jsonld",
"@type": "ldp:Container",
"@id": `store://local.${component.uniq}/${attribute}/`,
"ldp:contains": contains,
permissions: [{ mode: { "@type": "view" } }],
};
component.parameters[attribute] =
`store://local.${component.uniq}/${attribute}/`;
} else {
component.parameters[attribute] =
federation.values().next().value +
path.replace(/federation:\//, "");
}
}
}
component.parameters[attribute] = rewriteServer(component.parameters[attribute], {client: {server: config.client.server}});
}
/* Rewrite every parameters to kebab-case */
const rewriteParameters = {};
for (const [attribute, value] of Object.entries(component.parameters)) {
const attributeName = attribute.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, "-$1").toLowerCase();
if(!ignoredAttributes.includes(attributeName)) {
rewriteParameters[attributeName] = value;
}
}
component.attributes = rewriteParameters;
component.attributes.route = component.route;
component.attributes.uniq = component.uniq;
}
if (component.extensions) {
component.extensions = component.extensions.map((extension) => {
if (extension.parameters) {
const federation = new Set();
if (config.client.server) {
federation.add(config.client.server);
}
if (config.client.servers) {
for (const server of config.client.servers) {
federation.add(server);
}
}
if (component.federation) {
for (const target of component.federation) {
federation.add(target);
}
}
if (extension.federation) {
for (const target of extension.federation) {
federation.add(target);
}
}
extension.federation = [...federation];
for (const [attribute, path] of Object.entries(extension.parameters)) {
if (typeof path === "string") {
if (path.startsWith("federation://")) {
const contains = generateUrl(federation, path);
if (contains.length > 1) {
federations[`store://local.${extension.uniq}/${attribute}/`] = {
"@cache": "false",
"@context": "https://cdn.startinblox.com/owl/context.jsonld",
"@type": "ldp:Container",
"@id": `store://local.${extension.uniq}/${attribute}/`,
"ldp:contains": contains,
permissions: [{ mode: { "@type": "view" } }],
};
extension.parameters[attribute] =
`store://local.${extension.uniq}/${attribute}/`;
} else {
component.parameters[attribute] =
federation.values().next().value +
path.replace(/federation:\//, "");
}
}
}
component.parameters[attribute] = rewriteServer(component.parameters[attribute], {client: {server: config.client.server}});
}
/* Rewrite every parameters to kebab-case */
const rewriteParameters = {};
for (const [attribute, value] of Object.entries(extension.parameters)) {
const attributeName = attribute.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, "-$1").toLowerCase();
if(!ignoredAttributes.includes(attributeName)) {
rewriteParameters[attributeName] = value;
}
}
extension.attributes = rewriteParameters;
extension.attributes.uniq = extension.uniq;
}
return extension;
});
}
// Legacy: component.experimental to component.integration
if (component.experimental) {
component.integration = component.experimental;
}
return component;
});
const getRoute = (type, returnFirst = false, ignoreError = false) => {
const availables = components.filter(
(c) => c.type === type || c.uniq === type,
);
for (const c of components) {
if (c.extensions) {
for (const e of c.extensions) {
if (e.type === type || e.uniq === type) {
availables.push(e);
}
}
}
}
if (availables.length > 1) {
if (returnFirst) {
return availables[0].route;
}
return availables[availables.length - 1].route;
}
if (availables.length < 1) {
if (!ignoreError && import.meta.env.DEV)
console.error(`No component found for route ${type}`);
} else {
return availables[0].route;
}
return false;
};
const getDefaultRoute = () => {
const defaultComponent = components.filter(
(e) => e.defaultRoute !== undefined,
);
let defaultRoute = "dashboard";
if (defaultComponent.length === 1) {
defaultRoute = defaultComponent[0].route;
}
return defaultRoute;
};
const listFiles = (directory, subfolder) => {
try {
const files = readdirSync(directory);
const fileNames = files.filter((filePath) =>
statSync(join(directory, filePath)).isFile(),
);
return fileNames.map(
(file) => () => `${subfolder}/${file.replace(".hbs", "")}`,
);
} catch (err) {
console.error(err);
return [];
}
};
const notifications = listFiles(
"./src/partials/notifications",
"notifications",
);
const widgets = listFiles("./src/partials/widgets", "widgets");
const mandatoryComponents = defaultConfig.components.filter(
(component) =>
!config.components.some((c) => component["type"] === c["type"]),
);
const definitiveConfig = {
client: Object.fromEntries(
Object.entries(config.client).map(([k, c]) => [
k,
convertStringToBoolean(rewriteServer(c, config)),
]),
),
components: [...mandatoryComponents, ...config.components],
// Both handlebars and vite does not handle Set properly
componentSet: [...componentSet],
npm: requiredPackages.concat(config.npm),
federations,
files: {
notifications: notifications,
widgets: widgets,
},
helpers: {
getDefaultRoute,
getRoute,
listFiles,
},
};
export default definitiveConfig;