mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
add css utils, display form, enhance Text, add profile base format
* fix format logic with optional list * add forms display handling logic * add Image component * update Text component to work with Icons + enhance card and content style * add tailwind css utils for builder * add Profile page, builder component and display store * add profile page format * update profile test script with right value format to fit components props * add Collection component that is a builder with all component * misc enhancement
This commit is contained in:
parent
db3a6ac7bd
commit
6c8ac4b7a1
26 changed files with 936 additions and 158 deletions
|
|
@ -83,7 +83,7 @@ def ban_item(id: str, ip: str, reason: str, ban_start_date: int, ban_end_date: i
|
|||
|
||||
def bans_items(bans: Optional[list] = None) -> list:
|
||||
|
||||
if bans is None or len(bans) == 0:
|
||||
if bans is None or (isinstance(bans, list) and len(bans) == 0):
|
||||
return []
|
||||
|
||||
items = []
|
||||
|
|
@ -129,7 +129,7 @@ def bans_filters(reasons: Optional[list] = None, remains: Optional[list] = None)
|
|||
]
|
||||
|
||||
# Case "all" ans
|
||||
if reasons is not None and len(reasons) >= 2:
|
||||
if reasons is not None and (isinstance(reasons, list) and len(reasons) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -154,7 +154,7 @@ def bans_filters(reasons: Optional[list] = None, remains: Optional[list] = None)
|
|||
},
|
||||
)
|
||||
|
||||
if remains is not None and len(remains) >= 2:
|
||||
if remains is not None and (isinstance(remains, list) and len(remains) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -195,7 +195,7 @@ def fallback_message(msg: str, display: Optional[list] = None) -> dict:
|
|||
|
||||
def bans_list(bans: Optional[list] = None, reasons: Optional[list] = None, remains: Optional[list] = None) -> dict:
|
||||
|
||||
if bans is None or len(bans) == 0:
|
||||
if bans is None or (isinstance(bans, list) and len(bans) == 0):
|
||||
return fallback_message(msg="bans_not_found", display=["main", 0])
|
||||
|
||||
actions_table_list = [
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ def configs_filter(config_types: Optional[list] = None) -> list: # healths = "u
|
|||
},
|
||||
]
|
||||
|
||||
if config_types is not None and len(config_types) >= 2:
|
||||
if config_types is not None and (isinstance(config_types, list) and len(config_types) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -436,10 +436,10 @@ def fallback_message(msg: str, display: Optional[list] = None) -> dict:
|
|||
|
||||
def configs_builder(configs: Optional[list] = None, config_types: Optional[list] = None, services: Optional[list] = None) -> list:
|
||||
|
||||
if config_types is None or len(config_types) == 0:
|
||||
if config_types is None or (isinstance(config_types, list) and len(config_types) == 0):
|
||||
return [fallback_message(msg="configs_missing_types")]
|
||||
|
||||
if services is None or len(services) == 0:
|
||||
if services is None or (isinstance(services, list) and len(services) == 0):
|
||||
return [fallback_message(msg="configs_missing_services")]
|
||||
|
||||
configs_items = []
|
||||
|
|
@ -459,7 +459,7 @@ def configs_builder(configs: Optional[list] = None, config_types: Optional[list]
|
|||
)
|
||||
)
|
||||
|
||||
if configs is None or len(configs) == 0:
|
||||
if configs is None or (isinstance(configs, list) and len(configs) == 0):
|
||||
return [
|
||||
# Tabs is button group with display value and a size tab inside a tabs container
|
||||
configs_tabs(),
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ def instances_filter(healths: str, types: Optional[list] = None, methods: Option
|
|||
}
|
||||
]
|
||||
|
||||
if types is not None and len(types) >= 2:
|
||||
if types is not None and (isinstance(types, list) and len(types) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -75,7 +75,7 @@ def instances_filter(healths: str, types: Optional[list] = None, methods: Option
|
|||
}
|
||||
)
|
||||
|
||||
if methods is not None and len(methods) >= 2:
|
||||
if methods is not None and (isinstance(methods, list) and len(methods) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -100,7 +100,7 @@ def instances_filter(healths: str, types: Optional[list] = None, methods: Option
|
|||
},
|
||||
)
|
||||
|
||||
if healths is not None and len(healths) >= 2:
|
||||
if healths is not None and (isinstance(healths, list) and len(healths) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -396,7 +396,7 @@ def fallback_message(msg: str, display: Optional[list] = None) -> dict:
|
|||
|
||||
def instances_list(instances: Optional[list] = None, types: Optional[list] = None, methods: Optional[list] = None, healths: Optional[list] = None) -> dict:
|
||||
|
||||
if instances is None or len(instances) == 0:
|
||||
if instances is None or (isinstance(instances, list) and len(instances) == 0):
|
||||
return fallback_message(msg="instances_not_found", display=["main", 0])
|
||||
|
||||
items = []
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ from .utils.widgets import (
|
|||
regular_widget,
|
||||
unmatch_widget,
|
||||
pairs_widget,
|
||||
image_widget,
|
||||
)
|
||||
from .utils.table import add_column
|
||||
from .utils.format import get_fields_from_field
|
||||
|
|
@ -55,7 +56,7 @@ def fallback_message(msg: str, display: Optional[list] = None) -> dict:
|
|||
|
||||
def profile_info(user_profile: Optional[list] = None) -> dict:
|
||||
|
||||
if user_profile is None or len(user_profile) == 0:
|
||||
if user_profile is None or (isinstance(user_profile, list) and len(user_profile) == 0):
|
||||
return fallback_message("profile_info_not_found", display=["main", 0])
|
||||
|
||||
return {
|
||||
|
|
@ -67,9 +68,12 @@ def profile_info(user_profile: Optional[list] = None) -> dict:
|
|||
title="profile_info_title", # keep it (a18n)
|
||||
),
|
||||
subtitle_widget(
|
||||
subtitle="profile__info_subtitle", # keep it (a18n)
|
||||
subtitle="profile_info_subtitle", # keep it (a18n)
|
||||
),
|
||||
pairs_widget(
|
||||
pairs=user_profile,
|
||||
columns={"pc": 6, "tablet": 12, "mobile": 12},
|
||||
),
|
||||
pairs_widget(pairs=user_profile),
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -86,16 +90,34 @@ def profile_account_form(email: str) -> dict:
|
|||
subtitle_widget(
|
||||
subtitle="profile_account_subtitle", # keep it (a18n)
|
||||
),
|
||||
button_group_widget(
|
||||
buttons=[
|
||||
button_widget(
|
||||
text="profile_account_tab_email",
|
||||
display=["account", 0],
|
||||
size="normal",
|
||||
color="back",
|
||||
),
|
||||
button_widget(
|
||||
text="profile_account_tab_password",
|
||||
display=["account", 1],
|
||||
size="normal",
|
||||
color="back",
|
||||
),
|
||||
],
|
||||
buttonGroupClass="bouton-group-card mt-2",
|
||||
),
|
||||
regular_widget(
|
||||
display=["account", 0],
|
||||
title="profile_account_form_email_title",
|
||||
maxWidthScreen="xs",
|
||||
endpoint="/totp-enable",
|
||||
endpoint="/edit",
|
||||
method="POST",
|
||||
fields=[
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-email",
|
||||
name="email",
|
||||
type="password",
|
||||
label="profile_email", # keep it (a18n)
|
||||
value=email,
|
||||
pattern="", # add your pattern if needed
|
||||
|
|
@ -111,63 +133,16 @@ def profile_account_form(email: str) -> dict:
|
|||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-password",
|
||||
name="new_password",
|
||||
label="profile_password", # keep it (a18n)
|
||||
value="",
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_password_placeholder", # keep it (a18n)
|
||||
popovers=[
|
||||
{
|
||||
"iconName": "exclamation",
|
||||
"color": "yellow-darker",
|
||||
"text": "profile_password_warning_desc",
|
||||
},
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "profile_password_desc",
|
||||
},
|
||||
],
|
||||
)
|
||||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-password-confirm",
|
||||
name="new_password_confirm",
|
||||
label="profile_password_confirm", # keep it (a18n)
|
||||
value="",
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_password_confirm_placeholder", # keep it (a18n)
|
||||
popovers=[
|
||||
{
|
||||
"iconName": "exclamation",
|
||||
"color": "yellow-darker",
|
||||
"text": "profile_password_confirm_warning_desc",
|
||||
},
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "profile_password_confirm_desc",
|
||||
},
|
||||
],
|
||||
)
|
||||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-password",
|
||||
id="profile-password-email",
|
||||
name="current_password",
|
||||
label="profile_current_password", # keep it (a18n)
|
||||
value="",
|
||||
required=True,
|
||||
type="password",
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_current_password_placeholder", # keep it (a18n)
|
||||
popovers=[
|
||||
{
|
||||
"iconName": "exclamation",
|
||||
"color": "yellow-darker",
|
||||
"text": "profile_current_password_warning_desc",
|
||||
},
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "profile_current_password_desc",
|
||||
|
|
@ -179,13 +154,87 @@ def profile_account_form(email: str) -> dict:
|
|||
buttons=[
|
||||
button_widget(
|
||||
id="profile-account-submit",
|
||||
text="action_update",
|
||||
iconName="plus",
|
||||
text="action_edit",
|
||||
iconName="pen",
|
||||
iconColor="white",
|
||||
color="success",
|
||||
color="edit",
|
||||
size="normal",
|
||||
type="submit",
|
||||
)
|
||||
],
|
||||
),
|
||||
regular_widget(
|
||||
display=["account", 1],
|
||||
title="profile_account_form_password_title",
|
||||
maxWidthScreen="xs",
|
||||
endpoint="/edit",
|
||||
method="POST",
|
||||
fields=[
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-password-account",
|
||||
name="new_password",
|
||||
label="profile_new_password", # keep it (a18n)
|
||||
value="",
|
||||
type="password",
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_new_password_placeholder", # keep it (a18n)
|
||||
popovers=[
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "profile_new_password_desc",
|
||||
},
|
||||
],
|
||||
)
|
||||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-password-confirm",
|
||||
name="new_password_confirm",
|
||||
type="password",
|
||||
label="profile_new_password_confirm", # keep it (a18n)
|
||||
value="",
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_new_password_confirm_placeholder", # keep it (a18n)
|
||||
popovers=[
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "profile_new_password_confirm_desc",
|
||||
},
|
||||
],
|
||||
)
|
||||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-password-update",
|
||||
name="current_password",
|
||||
label="profile_current_password", # keep it (a18n)
|
||||
value="",
|
||||
type="password",
|
||||
required=True,
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_current_password_placeholder", # keep it (a18n)
|
||||
popovers=[
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "profile_current_password_desc",
|
||||
},
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
buttons=[
|
||||
button_widget(
|
||||
id="profile-account-submit",
|
||||
text="action_edit",
|
||||
iconName="pen",
|
||||
iconColor="white",
|
||||
color="edit",
|
||||
size="normal",
|
||||
type="submit",
|
||||
containerClass="flex justify-center",
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
@ -200,11 +249,11 @@ def totp_enable_form(
|
|||
|
||||
recovery_widgets = []
|
||||
|
||||
if is_recovery_refreshed and len(totp_recovery_codes) > 0:
|
||||
if is_recovery_refreshed and (totp_recovery_codes is not None and (isinstance(totp_recovery_codes, list) and len(totp_recovery_codes) > 0)):
|
||||
recovery_widgets.append(pairs_widget(pairs=totp_recovery_codes))
|
||||
|
||||
if is_recovery_refreshed and totp_recovery_codes is None or len(totp_recovery_codes) == 0:
|
||||
recovery_widgets.append(text_widget(text="profile_recovery_codes_refresh_but_not_found", color="error", iconName="", iconColor=""))
|
||||
if is_recovery_refreshed and (totp_recovery_codes is None or (isinstance(totp_recovery_codes, list) and len(totp_recovery_codes) == 0)):
|
||||
recovery_widgets.append(text_widget(text="profile_recovery_codes_refresh_but_not_found", iconName="", iconColor="error"))
|
||||
|
||||
recovery_widgets.append(
|
||||
button_group_widget(
|
||||
|
|
@ -238,25 +287,25 @@ def totp_enable_form(
|
|||
subtitle="profile_totp_subtitle", # keep it (a18n)
|
||||
),
|
||||
text_widget(
|
||||
text="profile_totp_enable", # keep it (a18n)
|
||||
icon="check",
|
||||
textClass="flex justify-center",
|
||||
text="profile_totp_enable_state", # keep it (a18n)
|
||||
iconName="check",
|
||||
iconColor="success",
|
||||
textIconContainerClass="col-span-12 flex justify-center items-center mt-2",
|
||||
),
|
||||
# totp secret (type password), totp code, password
|
||||
regular_widget(
|
||||
title="profile_totp_disable_title",
|
||||
subtitle="profile_totp_disable_subtitle",
|
||||
title="profile_totp_disable_form_title",
|
||||
maxWidthScreen="xs",
|
||||
endpoint="/totp-disable",
|
||||
method="POST",
|
||||
fields=[
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-totp-code",
|
||||
id="profile-totp-code-enable",
|
||||
name="totp_code",
|
||||
label="profile_totp_code", # keep it (a18n)
|
||||
value="",
|
||||
isClipboard=True,
|
||||
required=True,
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_totp_code_placeholder", # keep it (a18n)
|
||||
|
|
@ -270,11 +319,11 @@ def totp_enable_form(
|
|||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-totp-code",
|
||||
type="password",
|
||||
id="profile-totp-current-password",
|
||||
name="current_password",
|
||||
label="profile_current_password", # keep it (a18n)
|
||||
value="",
|
||||
isClipboard=True,
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_current_password_placeholder", # keep it (a18n)
|
||||
|
|
@ -290,9 +339,9 @@ def totp_enable_form(
|
|||
buttons=[
|
||||
button_widget(
|
||||
id="profile-disable-submit",
|
||||
text="action_enable",
|
||||
iconName="plus",
|
||||
iconColor="check",
|
||||
text="action_disable",
|
||||
iconName="cross",
|
||||
iconColor="white",
|
||||
color="success",
|
||||
size="normal",
|
||||
type="submit",
|
||||
|
|
@ -316,9 +365,10 @@ def totp_disable_form(totp_img: str = "", totp_secret: str = "") -> dict:
|
|||
subtitle="profile_totp_subtitle", # keep it (a18n)
|
||||
),
|
||||
text_widget(
|
||||
text="profile_totp_disable", # keep it (a18n)
|
||||
icon="cross",
|
||||
textClass="flex justify-center",
|
||||
text="profile_totp_disable_state", # keep it (a18n)
|
||||
iconName="uncheck",
|
||||
iconColor="error",
|
||||
textIconContainerClass="col-span-12 flex justify-center items-center mt-2",
|
||||
),
|
||||
image_widget(
|
||||
src=totp_img,
|
||||
|
|
@ -326,6 +376,7 @@ def totp_disable_form(totp_img: str = "", totp_secret: str = "") -> dict:
|
|||
),
|
||||
# totp secret (type password), totp code, password
|
||||
regular_widget(
|
||||
title="profile_totp_enable_form_title",
|
||||
maxWidthScreen="xs",
|
||||
endpoint="/edit",
|
||||
method="POST",
|
||||
|
|
@ -351,11 +402,11 @@ def totp_disable_form(totp_img: str = "", totp_secret: str = "") -> dict:
|
|||
),
|
||||
get_fields_from_field(
|
||||
input_widget(
|
||||
id="profile-totp-code",
|
||||
id="profile-totp-code-disabled",
|
||||
name="totp_code",
|
||||
label="profile_totp_code", # keep it (a18n)
|
||||
value="",
|
||||
isClipboard=True,
|
||||
required=True,
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_totp_code_placeholder", # keep it (a18n)
|
||||
|
|
@ -373,7 +424,8 @@ def totp_disable_form(totp_img: str = "", totp_secret: str = "") -> dict:
|
|||
name="current_password",
|
||||
label="profile_current_password", # keep it (a18n)
|
||||
value="",
|
||||
isClipboard=True,
|
||||
required=True,
|
||||
type="password",
|
||||
pattern="", # add your pattern if needed
|
||||
columns={"pc": 12, "tablet": 12, "mobile": 12},
|
||||
placeholder="profile_current_password_placeholder", # keep it (a18n)
|
||||
|
|
@ -391,7 +443,7 @@ def totp_disable_form(totp_img: str = "", totp_secret: str = "") -> dict:
|
|||
id="profile-disable-submit",
|
||||
text="action_enable",
|
||||
iconName="plus",
|
||||
iconColor="check",
|
||||
iconColor="white",
|
||||
color="success",
|
||||
size="normal",
|
||||
type="submit",
|
||||
|
|
@ -451,7 +503,7 @@ def fallback_message(msg: str, display: Optional[list] = None) -> dict:
|
|||
|
||||
def profile_builder(user: Optional[dict] = None) -> list:
|
||||
|
||||
if user is None or len(user) == 0:
|
||||
if user is None or (isinstance(user, list) and len(user) == 0):
|
||||
return [fallback_message("profile_user_not_found")]
|
||||
|
||||
totp_data = user.get("totp", None)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def reports_filters(reasons: Optional[list] = None, countries: Optional[list] =
|
|||
},
|
||||
]
|
||||
|
||||
if reasons is not None and len(reasons) >= 2:
|
||||
if reasons is not None and (isinstance(reasons, list) and len(reasons) >= 2):
|
||||
reports_filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -66,7 +66,7 @@ def reports_filters(reasons: Optional[list] = None, countries: Optional[list] =
|
|||
}
|
||||
)
|
||||
|
||||
if countries is not None and len(countries) >= 2:
|
||||
if countries is not None and (isinstance(countries, list) and len(countries) >= 2):
|
||||
reports_filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -91,7 +91,7 @@ def reports_filters(reasons: Optional[list] = None, countries: Optional[list] =
|
|||
}
|
||||
)
|
||||
|
||||
if methods is not None and len(methods) >= 2:
|
||||
if methods is not None and (isinstance(methods, list) and len(methods) >= 2):
|
||||
reports_filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -116,7 +116,7 @@ def reports_filters(reasons: Optional[list] = None, countries: Optional[list] =
|
|||
}
|
||||
)
|
||||
|
||||
if codes is not None and len(codes) >= 2:
|
||||
if codes is not None and (isinstance(codes, list) and len(codes) >= 2):
|
||||
reports_filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -185,7 +185,7 @@ def reports_builder(
|
|||
reports: list, reasons: Optional[list] = None, countries: Optional[list] = None, methods: Optional[list] = None, codes: Optional[list] = None
|
||||
) -> str:
|
||||
|
||||
if reports is None or len(reports) == 0:
|
||||
if reports is None or (isinstance(reports, list) and len(reports) == 0):
|
||||
return [fallback_message("reports_not_found")]
|
||||
|
||||
reports_items = [report_item(**report, id=index) for index, report in enumerate(reports)]
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ def users_filter(roles: Optional[list] = None, totp_states: Optional[list] = Non
|
|||
}
|
||||
]
|
||||
|
||||
if roles is not None and len(roles) >= 2:
|
||||
if roles is not None and (isinstance(roles, list) and len(roles) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -85,7 +85,7 @@ def users_filter(roles: Optional[list] = None, totp_states: Optional[list] = Non
|
|||
}
|
||||
)
|
||||
|
||||
if totp_states is not None and len(totp_states) >= 2:
|
||||
if totp_states is not None and (isinstance(totp_states, list) and len(totp_states) >= 2):
|
||||
filters.append(
|
||||
{
|
||||
"type": "=",
|
||||
|
|
@ -399,14 +399,14 @@ def user_management_builder(
|
|||
users: Optional[list] = None, roles: Optional[list] = None, roles_form: Optional[list] = None, totp_states: Optional[list] = None
|
||||
) -> list:
|
||||
|
||||
if roles is None or len(roles) == 0 or roles_form is None or len(roles_form) == 0:
|
||||
if roles is None or (isinstance(roles, list) and len(roles) == 0) or roles_form is None or (isinstance(roles_form, list) and len(roles_form) == 0):
|
||||
return [fallback_message("user_management_missing_roles")]
|
||||
|
||||
users_items = []
|
||||
users_forms = []
|
||||
users_forms.append(user_management_form(is_new=True, display_index=1, role="", username="", roles=roles_form))
|
||||
|
||||
if users is None or len(users) == 0:
|
||||
if users is None or (isinstance(users, list) and len(users) == 0):
|
||||
return [
|
||||
# Tabs is button group with display value and a size tab inside a tabs container
|
||||
user_management_tabs(),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ def advanced_widget(
|
|||
endpoint: str = "",
|
||||
method: str = "POST",
|
||||
oldServerName: str = "",
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"}
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"},
|
||||
display: Optional[list] = None
|
||||
):
|
||||
"""
|
||||
This component is used to create a complete advanced form with plugin selection.
|
||||
|
|
@ -28,6 +29,7 @@ def advanced_widget(
|
|||
- `method` **String** Http method to be used on form submit. (optional, default `"POST"`)
|
||||
- `oldServerName` **String** Old server name. This is a server name before any changes. (optional, default `""`)
|
||||
- `columns` **Object** Columns object. (optional, default `{"pc":"12","tablet":"12","mobile":"12"}`)
|
||||
- `display` **Array** Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef. (optional, default `[]`)
|
||||
|
||||
EXAMPLE
|
||||
|
||||
|
|
@ -66,7 +68,7 @@ def advanced_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("containerClass", containerClass, ""),("operation", operation, "edit"),("endpoint", endpoint, ""),("method", method, "POST"),("oldServerName", oldServerName, ""),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"})]
|
||||
list_params = [("containerClass", containerClass, ""),("operation", operation, "edit"),("endpoint", endpoint, ""),("method", method, "POST"),("oldServerName", oldServerName, ""),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"}),("display", display, None)]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -139,7 +141,7 @@ def button_widget(
|
|||
|
||||
def button_group_widget(
|
||||
buttons: list,
|
||||
boutonGroupClass: str = ""
|
||||
buttonGroupClass: str = ""
|
||||
):
|
||||
"""
|
||||
This component allow to display multiple buttons in the same row using flex.
|
||||
|
|
@ -149,7 +151,7 @@ def button_group_widget(
|
|||
PARAMETERS
|
||||
|
||||
- `buttons` **Array** List of buttons to display. Button component is used.
|
||||
- `boutonGroupClass` **String** Additional class for the flex container (optional, default `""`)
|
||||
- `buttonGroupClass` **String** Additional class for the flex container (optional, default `""`)
|
||||
|
||||
EXAMPLE
|
||||
|
||||
|
|
@ -188,7 +190,7 @@ def button_group_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("boutonGroupClass", boutonGroupClass, "")]
|
||||
list_params = [("buttonGroupClass", buttonGroupClass, "")]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -635,7 +637,8 @@ def easy_widget(
|
|||
endpoint: str = "",
|
||||
method: str = "POST",
|
||||
oldServerName: str = "",
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"}
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"},
|
||||
display: Optional[list] = None
|
||||
):
|
||||
"""
|
||||
This component is used to create a complete easy form with plugin selection.
|
||||
|
|
@ -649,6 +652,7 @@ def easy_widget(
|
|||
- `method` **String** Http method to be used on form submit. (optional, default `"POST"`)
|
||||
- `oldServerName` **String** Old server name. This is a server name before any changes. (optional, default `""`)
|
||||
- `columns` **Object** Columns object. (optional, default `{"pc":"12","tablet":"12","mobile":"12"}`)
|
||||
- `display` **Array** Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef. (optional, default `[]`)
|
||||
|
||||
EXAMPLE
|
||||
|
||||
|
|
@ -687,7 +691,7 @@ def easy_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("containerClass", containerClass, ""),("operation", operation, "edit"),("endpoint", endpoint, ""),("method", method, "POST"),("oldServerName", oldServerName, ""),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"})]
|
||||
list_params = [("containerClass", containerClass, ""),("operation", operation, "edit"),("endpoint", endpoint, ""),("method", method, "POST"),("oldServerName", oldServerName, ""),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"}),("display", display, None)]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -1069,6 +1073,47 @@ def icons_widget(
|
|||
return { "type" : "Icons", "data" : data }
|
||||
|
||||
|
||||
def image_widget(
|
||||
src: str,
|
||||
alt: str = "",
|
||||
imgClass: str = "",
|
||||
imgContainerClass: str = "",
|
||||
attrs: Optional[dict] = None
|
||||
):
|
||||
"""
|
||||
This component is used for regular paragraph.
|
||||
|
||||
PARAMETERS
|
||||
|
||||
- `src` **String** The src value of the image.
|
||||
- `alt` **String** The alt value of the image. Can be a translation key or by default raw text. (optional, default `""`)
|
||||
- `imgClass` **String** (optional, default `""`)
|
||||
- `imgContainerClass` **String** (optional, default `""`)
|
||||
- `attrs` **Object** List of attributes to add to the image. (optional, default `{}`)
|
||||
|
||||
EXAMPLE
|
||||
|
||||
{
|
||||
src: "https://via.placeholder.com/150",
|
||||
alt: "My image",
|
||||
attrs: { id: "paragraph" },
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
data = {
|
||||
"src" : src,
|
||||
}
|
||||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("alt", alt, ""),("imgClass", imgClass, ""),("imgContainerClass", imgContainerClass, ""),("attrs", attrs, None)]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
return { "type" : "Image", "data" : data }
|
||||
|
||||
|
||||
def input_widget(
|
||||
label: str,
|
||||
name: str,
|
||||
|
|
@ -1569,7 +1614,8 @@ def raw_widget(
|
|||
containerClass: str = "",
|
||||
endpoint: str = "",
|
||||
method: str = "POST",
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"}
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"},
|
||||
display: Optional[list] = None
|
||||
):
|
||||
"""
|
||||
This component is used to create a complete raw form with settings as JSON format.
|
||||
|
|
@ -1583,6 +1629,7 @@ def raw_widget(
|
|||
- `endpoint` **String** Form endpoint. Case none, will be ignored. (optional, default `""`)
|
||||
- `method` **String** Http method to be used on form submit. (optional, default `"POST"`)
|
||||
- `columns` **Object** Columns object. (optional, default `{"pc":"12","tablet":"12","mobile":"12"}`)
|
||||
- `display` **Array** Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef. (optional, default `[]`)
|
||||
|
||||
EXAMPLE
|
||||
|
||||
|
|
@ -1602,7 +1649,7 @@ def raw_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("operation", operation, "edit"),("oldServerName", oldServerName, ""),("containerClass", containerClass, ""),("endpoint", endpoint, ""),("method", method, "POST"),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"})]
|
||||
list_params = [("operation", operation, "edit"),("oldServerName", oldServerName, ""),("containerClass", containerClass, ""),("endpoint", endpoint, ""),("method", method, "POST"),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"}),("display", display, None)]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -1612,9 +1659,12 @@ def raw_widget(
|
|||
def regular_widget(
|
||||
fields: dict,
|
||||
buttons: dict,
|
||||
title: str = "",
|
||||
subtitle: str = "",
|
||||
containerClass: str = "",
|
||||
endpoint: str = "",
|
||||
method: str = "POST",
|
||||
display: Optional[list] = None,
|
||||
maxWidthScreen: str = "lg",
|
||||
columns: dict = {"pc":"12","tablet":"12","mobile":"12"}
|
||||
):
|
||||
|
|
@ -1623,11 +1673,14 @@ def regular_widget(
|
|||
|
||||
PARAMETERS
|
||||
|
||||
- `title` **String** Form title (optional, default `""`)
|
||||
- `subtitle` **String** Form subtitle (optional, default `""`)
|
||||
- `fields` **Object** List of Fields component to display
|
||||
- `buttons` **Object** We need to send a regular ButtonGroup buttons prop
|
||||
- `containerClass` **String** Container additional class (optional, default `""`)
|
||||
- `endpoint` **String** Form endpoint. Case none, will be ignored. (optional, default `""`)
|
||||
- `method` **String** Http method to be used on form submit. (optional, default `"POST"`)
|
||||
- `display` **Array** Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef. (optional, default `[]`)
|
||||
- `maxWidthScreen` **String** Max screen width for the settings based on the breakpoint (xs, sm, md, lg, xl, 2xl) (optional, default `"lg"`)
|
||||
- `columns` **Object** Columns object. (optional, default `{"pc":"12","tablet":"12","mobile":"12"}`)
|
||||
|
||||
|
|
@ -1667,7 +1720,7 @@ def regular_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("containerClass", containerClass, ""),("endpoint", endpoint, ""),("method", method, "POST"),("maxWidthScreen", maxWidthScreen, "lg"),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"})]
|
||||
list_params = [("title", title, ""),("subtitle", subtitle, ""),("containerClass", containerClass, ""),("endpoint", endpoint, ""),("method", method, "POST"),("display", display, None),("maxWidthScreen", maxWidthScreen, "lg"),("columns", columns, {"pc":"12","tablet":"12","mobile":"12"})]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -2009,6 +2062,7 @@ def tabulator_widget(
|
|||
itemsBeforePagination: int = 10,
|
||||
paginationSize: int = 10,
|
||||
paginationInitialPage: int = 1,
|
||||
paginationButtonCount: int = 3,
|
||||
paginationSizeSelector: list = [10,25,50,100]
|
||||
):
|
||||
"""
|
||||
|
|
@ -2037,6 +2091,7 @@ def tabulator_widget(
|
|||
- `itemsBeforePagination` **Number** Hide pagination unless number is reach. (optional, default `10`)
|
||||
- `paginationSize` **Number** Number of items per page (optional, default `10`)
|
||||
- `paginationInitialPage` **Number** Initial page (optional, default `1`)
|
||||
- `paginationButtonCount` **Number** Available pagination buttons (optional, default `3`)
|
||||
- `paginationSizeSelector` **Array** Select number of items per page (optional, default `[10,25,50,100]`)
|
||||
|
||||
EXAMPLE
|
||||
|
|
@ -2067,7 +2122,7 @@ def tabulator_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("isStriped", isStriped, True),("filters", filters, None),("actionsButtons", actionsButtons, None),("layout", layout, "fitDataTable"),("rowHeight", rowHeight, 0),("colMinWidth", colMinWidth, 150),("colMaxWidth", colMaxWidth, 0),("isPagination", isPagination, True),("itemsBeforePagination", itemsBeforePagination, 10),("paginationSize", paginationSize, 10),("paginationInitialPage", paginationInitialPage, 1),("paginationSizeSelector", paginationSizeSelector, [10,25,50,100])]
|
||||
list_params = [("isStriped", isStriped, True),("filters", filters, None),("actionsButtons", actionsButtons, None),("layout", layout, "fitDataTable"),("rowHeight", rowHeight, 0),("colMinWidth", colMinWidth, 150),("colMaxWidth", colMaxWidth, 0),("isPagination", isPagination, True),("itemsBeforePagination", itemsBeforePagination, 10),("paginationSize", paginationSize, 10),("paginationInitialPage", paginationInitialPage, 1),("paginationButtonCount", paginationButtonCount, 3),("paginationSizeSelector", paginationSizeSelector, [10,25,50,100])]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -2077,7 +2132,8 @@ def tabulator_widget(
|
|||
def templates_widget(
|
||||
templates: dict,
|
||||
operation: str = "edit",
|
||||
oldServerName: str = ""
|
||||
oldServerName: str = "",
|
||||
display: Optional[list] = None
|
||||
):
|
||||
"""
|
||||
This component is used to create a complete settings form with all modes (advanced, raw, easy).
|
||||
|
|
@ -2087,6 +2143,7 @@ def templates_widget(
|
|||
- `templates` **Object** List of advanced templates that contains settings. Must be a dict with mode as key, then the template name as key with a list of data (different for each modes).
|
||||
- `operation` **String** Operation type (edit, new, delete). (optional, default `"edit"`)
|
||||
- `oldServerName` **String** Old server name. This is a server name before any changes. (optional, default `""`)
|
||||
- `display` **Array** Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef. (optional, default `[]`)
|
||||
|
||||
EXAMPLE
|
||||
|
||||
|
|
@ -2109,7 +2166,7 @@ def templates_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("operation", operation, "edit"),("oldServerName", oldServerName, "")]
|
||||
list_params = [("operation", operation, "edit"),("oldServerName", oldServerName, ""),("display", display, None)]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -2119,7 +2176,10 @@ def templates_widget(
|
|||
def text_widget(
|
||||
text: str,
|
||||
textClass: str = "",
|
||||
textIconContainerClass: str = "col-span-12 flex justify-center items-center",
|
||||
color: str = "",
|
||||
iconName: str = "",
|
||||
iconColor: str = "",
|
||||
bold: bool = False,
|
||||
uppercase: bool = False,
|
||||
tag: str = "p",
|
||||
|
|
@ -2133,7 +2193,10 @@ def text_widget(
|
|||
|
||||
- `text` **String** The text value. Can be a translation key or by default raw text.
|
||||
- `textClass` **String** Style of text. Can be replace by any class starting by 'text-' like 'text-stat'. (optional, default `""`)
|
||||
- `textIconContainerClass` **String** Case we have icon with text, we wrap the text on a container with the icon. We can add a class to this container. (optional, default `"col-span-12 flex justify-center items-center"`)
|
||||
- `color` **String** The color of the text between error, success, warning, info or tailwind color (optional, default `""`)
|
||||
- `iconName` **String** The name of the icon to display before the text. (optional, default `""`)
|
||||
- `iconColor` **String** The color of the icon. (optional, default `""`)
|
||||
- `bold` **Boolean** If the text should be bold or not. (optional, default `false`)
|
||||
- `uppercase` **Boolean** If the text should be uppercase or not. (optional, default `false`)
|
||||
- `tag` **String** The tag of the text. Can be p, span, div, h1, h2, h3, h4, h5, h6 (optional, default `"p"`)
|
||||
|
|
@ -2156,7 +2219,7 @@ def text_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("textClass", textClass, ""),("color", color, ""),("bold", bold, False),("uppercase", uppercase, False),("tag", tag, "p"),("icon", icon, False),("attrs", attrs, None)]
|
||||
list_params = [("textClass", textClass, ""),("textIconContainerClass", textIconContainerClass, "col-span-12 flex justify-center items-center"),("color", color, ""),("iconName", iconName, ""),("iconColor", iconColor, ""),("bold", bold, False),("uppercase", uppercase, False),("tag", tag, "p"),("icon", icon, False),("attrs", attrs, None)]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
@ -2212,6 +2275,8 @@ def title_widget(
|
|||
|
||||
def unmatch_widget(
|
||||
text: str,
|
||||
iconName: str = "search",
|
||||
iconColor: str = "",
|
||||
unmatchClass: str = ""
|
||||
):
|
||||
"""
|
||||
|
|
@ -2221,6 +2286,8 @@ def unmatch_widget(
|
|||
PARAMETERS
|
||||
|
||||
- `text` **String** The text to display
|
||||
- `iconName` **String** The icon to display (optional, default `"search"`)
|
||||
- `iconColor` **String** The color of the icon (optional, default `""`)
|
||||
- `unmatchClass` **String** The class to apply to the message. If not provided, the class will be based on the parent component. (optional, default `""`)
|
||||
|
||||
EXAMPLE
|
||||
|
|
@ -2237,7 +2304,7 @@ def unmatch_widget(
|
|||
|
||||
|
||||
# List of params that will be add only if not default value
|
||||
list_params = [("unmatchClass", unmatchClass, "")]
|
||||
list_params = [("iconName", iconName, "search"),("iconColor", iconColor, ""),("unmatchClass", unmatchClass, "")]
|
||||
for param in list_params:
|
||||
add_key_value(data, param[0], param[1], param[2])
|
||||
|
||||
|
|
|
|||
|
|
@ -14,21 +14,19 @@ from pages.profile import profile_builder
|
|||
# - last_update (last time update user info)
|
||||
user = {
|
||||
"profile": [
|
||||
{
|
||||
"profile_username": "username",
|
||||
"profile_email": "email",
|
||||
"profile_created_method": "created_method",
|
||||
"role": "admin",
|
||||
"role_description": "role_description",
|
||||
"permissions": "read, write, admin",
|
||||
"creation_date": "date",
|
||||
"last_update": "date",
|
||||
}
|
||||
{"key": "profile_username", "value": "username"},
|
||||
{"key": "profile_email", "value": "email"},
|
||||
{"key": "profile_created_method", "value": "created_method"},
|
||||
{"key": "profile_role", "value": "admin"},
|
||||
{"key": "profile_role_description", "value": "role_description"},
|
||||
{"key": "profile_permissions", "value": "read, write, admin"},
|
||||
{"key": "profile_creation_date", "value": "date"},
|
||||
{"key": "profile_last_update", "value": "date"},
|
||||
],
|
||||
"totp": {
|
||||
"is_totp": False,
|
||||
"totp_image": "", # base 64 that will be add in an img tag src
|
||||
"totp_recovery_codes": [{"0": "code_0"}, {"1": "code_1"}, {"2": "code_2"}],
|
||||
"totp_recovery_codes": [{"key": "0", "value": "code_0"}, {"key": "1", "value": "code_1"}, {"key": "2", "value": "code_2"}],
|
||||
"is_recovery_refreshed": False,
|
||||
"totp_secret": "totp_secret",
|
||||
},
|
||||
|
|
|
|||
153
src/ui/client/dashboard/components/Builder/Collection.vue
Normal file
153
src/ui/client/dashboard/components/Builder/Collection.vue
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<script setup>
|
||||
// Containers
|
||||
import Alert from "@components/Widget/Alert.vue";
|
||||
import Button from "@components/Widget/Button.vue";
|
||||
import ButtonGroup from "@components/Widget/ButtonGroup.vue";
|
||||
import Cell from "@components/Widget/Cell.vue";
|
||||
import Container from "@components/Widget/Container.vue";
|
||||
import Filter from "@components/Widget/Filter.vue";
|
||||
import Grid from "@components/Widget/Grid.vue";
|
||||
import GridLayout from "@components/Widget/GridLayout.vue";
|
||||
import Icons from "@components/Widget/Icons.vue";
|
||||
import Modal from "@components/Widget/Modal.vue";
|
||||
import Popover from "@components/Widget/Popover.vue";
|
||||
import PopoverGroup from "@components/Widget/PopoverGroup.vue";
|
||||
import Stat from "@components/Widget/Stat.vue";
|
||||
import Status from "@components/Widget/Status.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import Table from "@components/Widget/Table.vue";
|
||||
import Tabulator from "@components/Widget/Tabulator.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Regular from "@components/Form/Regular.vue";
|
||||
import Advanced from "@components/Form/Advanced.vue";
|
||||
import Raw from "@components/Form/Raw.vue";
|
||||
import Easy from "@components/Form/Easy.vue";
|
||||
import Fields from "@components/Form/Fields.vue";
|
||||
import Pairs from "@components/List/Pairs.vue";
|
||||
import Details from "@components/List/Details.vue";
|
||||
import Templates from "@components/Form/Templates.vue";
|
||||
import Unmatch from "@components/Message/Unmatch.vue";
|
||||
import { useEqualStr } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
* @name Builder/Bans.vue
|
||||
* @description This component is lightweight builder containing only the necessary components to create the bans page.
|
||||
* @example
|
||||
* [
|
||||
* {
|
||||
* type: "card",
|
||||
* gridLayoutClass: "transparent",
|
||||
* widgets: [
|
||||
* { type: "Unmatch",
|
||||
* data: { text: "bans_not_found" }
|
||||
* },
|
||||
* ],
|
||||
* },
|
||||
* ];
|
||||
* @param {array} builder - Array of containers and widgets
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
builder: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- top level grid (layout) -->
|
||||
<GridLayout
|
||||
v-for="(container, index) in props.builder"
|
||||
:key="index"
|
||||
:gridLayoutClass="container.containerClass"
|
||||
:maxWidthScreen="container.maxWidthScreen"
|
||||
:type="container.type"
|
||||
:title="container.title"
|
||||
:link="container.link"
|
||||
:columns="container.containerColumns"
|
||||
:id="container.id"
|
||||
:display="container.display"
|
||||
>
|
||||
<!-- widget grid -->
|
||||
<Grid>
|
||||
<!-- widget element -->
|
||||
<template v-for="(widget, index) in container.widgets" :key="index">
|
||||
<Title v-if="useEqualStr(widget.type, 'Title')" v-bind="widget.data" />
|
||||
<Subtitle
|
||||
v-if="useEqualStr(widget.type, 'Subtitle')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Unmatch
|
||||
v-if="useEqualStr(widget.type, 'Unmatch')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Tabulator
|
||||
v-if="useEqualStr(widget.type, 'Tabulator')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Button
|
||||
v-if="useEqualStr(widget.type, 'Button')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Regular
|
||||
v-if="useEqualStr(widget.type, 'Regular')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<ButtonGroup
|
||||
v-if="useEqualStr(widget.type, 'ButtonGroup')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<
|
||||
<Alert v-if="useEqualStr(widget.type, 'Alert')" v-bind="widget.data" />
|
||||
<Cell v-if="useEqualStr(widget.type, 'Cell')" v-bind="widget.data" />
|
||||
<Container
|
||||
v-if="useEqualStr(widget.type, 'Container')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Filter
|
||||
v-if="useEqualStr(widget.type, 'Filter')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Icons v-if="useEqualStr(widget.type, 'Icons')" v-bind="widget.data" />
|
||||
<Modal v-if="useEqualStr(widget.type, 'Modal')" v-bind="widget.data" />
|
||||
<Popover
|
||||
v-if="useEqualStr(widget.type, 'Popover')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
|
||||
<PopoverGroup
|
||||
v-if="useEqualStr(widget.type, 'PopoverGroup')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Stat v-if="useEqualStr(widget.type, 'Stat')" v-bind="widget.data" />
|
||||
<Status
|
||||
v-if="useEqualStr(widget.type, 'Status')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Table v-if="useEqualStr(widget.type, 'Table')" v-bind="widget.data" />
|
||||
<Text v-if="useEqualStr(widget.type, 'Text')" v-bind="widget.data" />
|
||||
<Advanced
|
||||
v-if="useEqualStr(widget.type, 'Advanced')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Raw v-if="useEqualStr(widget.type, 'Raw')" v-bind="widget.data" />
|
||||
<Easy v-if="useEqualStr(widget.type, 'Easy')" v-bind="widget.data" />
|
||||
<Fields
|
||||
v-if="useEqualStr(widget.type, 'Fields')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Pairs v-if="useEqualStr(widget.type, 'Pairs')" v-bind="widget.data" />
|
||||
<Details
|
||||
v-if="useEqualStr(widget.type, 'Details')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Templates
|
||||
v-if="useEqualStr(widget.type, 'Templates')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</GridLayout>
|
||||
</template>
|
||||
81
src/ui/client/dashboard/components/Builder/Profile.vue
Normal file
81
src/ui/client/dashboard/components/Builder/Profile.vue
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<script setup>
|
||||
// Containers
|
||||
import Grid from "@components/Widget/Grid.vue";
|
||||
import GridLayout from "@components/Widget/GridLayout.vue";
|
||||
import Tabulator from "@components/Widget/Tabulator.vue";
|
||||
import Button from "@components/Widget/Button.vue";
|
||||
import ButtonGroup from "@components/Widget/ButtonGroup.vue";
|
||||
import Regular from "@components/Form/Regular.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
import Image from "@components/Widget/Image.vue";
|
||||
import Unmatch from "@components/Message/Unmatch.vue";
|
||||
import Pairs from "@components/List/Pairs.vue";
|
||||
import { useEqualStr } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
* @name Builder/Profile.vue
|
||||
* @description This component is lightweight builder containing only the necessary components to create the profile page.
|
||||
** @example
|
||||
** @param {array} builder - Array of containers and widgets
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
builder: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- top level grid (layout) -->
|
||||
<GridLayout
|
||||
v-for="(container, index) in props.builder"
|
||||
:key="index"
|
||||
:gridLayoutClass="container.containerClass"
|
||||
:maxWidthScreen="container.maxWidthScreen"
|
||||
:type="container.type"
|
||||
:title="container.title"
|
||||
:link="container.link"
|
||||
:columns="container.containerColumns"
|
||||
:id="container.id"
|
||||
:display="container.display"
|
||||
>
|
||||
<!-- widget grid -->
|
||||
<Grid>
|
||||
<!-- widget element -->
|
||||
<template v-for="(widget, index) in container.widgets" :key="index">
|
||||
<Title v-if="useEqualStr(widget.type, 'Title')" v-bind="widget.data" />
|
||||
<Image v-if="useEqualStr(widget.type, 'Image')" v-bind="widget.data" />
|
||||
<Text v-if="useEqualStr(widget.type, 'Text')" v-bind="widget.data" />
|
||||
<Subtitle
|
||||
v-if="useEqualStr(widget.type, 'Subtitle')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Pairs v-if="useEqualStr(widget.type, 'Pairs')" v-bind="widget.data" />
|
||||
<Unmatch
|
||||
v-if="useEqualStr(widget.type, 'Unmatch')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Tabulator
|
||||
v-if="useEqualStr(widget.type, 'Tabulator')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Button
|
||||
v-if="useEqualStr(widget.type, 'Button')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<Regular
|
||||
v-if="useEqualStr(widget.type, 'Regular')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
<ButtonGroup
|
||||
v-if="useEqualStr(widget.type, 'ButtonGroup')"
|
||||
v-bind="widget.data"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</GridLayout>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { defineProps, reactive, onMounted, onUnmounted, computed } from "vue";
|
||||
import { defineProps, reactive, onMounted, onUnmounted, watch } from "vue";
|
||||
import Unmatch from "@components/Message/Unmatch.vue";
|
||||
import Container from "@components/Widget/Container.vue";
|
||||
import Fields from "@components/Form/Fields.vue";
|
||||
|
|
@ -14,6 +14,7 @@ import { plugin_types } from "@utils/variables";
|
|||
import { useAdvancedForm } from "@store/form.js";
|
||||
import { useCheckPluginsValidity } from "@utils/global.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useDisplayStore } from "@store/global.js";
|
||||
|
||||
/**
|
||||
* @name Form/Advanced.vue
|
||||
|
|
@ -52,6 +53,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
* @param {String} [method="POST"] - Http method to be used on form submit.
|
||||
* @param {String} [oldServerName=""] - Old server name. This is a server name before any changes.
|
||||
* @param {Object} [columns={ "pc": "12", "tablet": "12", "mobile": "12" }] - Columns object.
|
||||
* @param {Array} [display=[]] - Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef.
|
||||
*/
|
||||
|
||||
const advancedForm = useAdvancedForm();
|
||||
|
|
@ -93,8 +95,15 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: { pc: 12, tablet: 12, mobile: 12 },
|
||||
},
|
||||
display: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
const displayStore = useDisplayStore();
|
||||
|
||||
const data = reactive({
|
||||
currPlugin: "",
|
||||
plugins: [],
|
||||
|
|
@ -103,8 +112,31 @@ const data = reactive({
|
|||
isReqErr: false,
|
||||
settingErr: "",
|
||||
pluginErr: "",
|
||||
isDisplay: props.display.length
|
||||
? displayStore.isCurrentDisplay(props.display[0], props.display[1])
|
||||
: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @name checkDisplay
|
||||
* @description Check if the current display value is matching the display store value.
|
||||
* @returns {Void}
|
||||
*/
|
||||
function checkDisplay() {
|
||||
if (!props.display.length) return;
|
||||
data.isDisplay = displayStore.isCurrentDisplay(
|
||||
props.display[0],
|
||||
props.display[1]
|
||||
);
|
||||
}
|
||||
|
||||
// Case we have set a display group name and component id, the component id must match the current display id for the same group name to be displayed.
|
||||
if (props.display.length) {
|
||||
watch(displayStore.display, (val) => {
|
||||
checkDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
const comboboxPlugin = {
|
||||
id: `advanced-combobox-${uuidv4()}`,
|
||||
name: `advanced-combobox-${uuidv4()}`,
|
||||
|
|
@ -313,6 +345,7 @@ onUnmounted(() => {
|
|||
|
||||
<template>
|
||||
<Container
|
||||
v-if="data.isDisplay"
|
||||
data-advanced-form
|
||||
data-is="form"
|
||||
:tag="'form'"
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ import Container from "@components/Widget/Container.vue";
|
|||
import Fields from "@components/Form/Fields.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import ButtonGroup from "@components/Widget/ButtonGroup.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
import GroupMultiple from "@components/Forms/Group/Multiple.vue";
|
||||
import Unmatch from "@components/Message/Unmatch.vue";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useCheckPluginsValidity } from "@utils/global.js";
|
||||
import { useEasyForm } from "@store/form.js";
|
||||
import { useDisplayStore } from "@store/global.js";
|
||||
|
||||
/**
|
||||
* @name Form/Easy.vue
|
||||
|
|
@ -56,6 +56,7 @@ import { useEasyForm } from "@store/form.js";
|
|||
* @param {String} [method="POST"] - Http method to be used on form submit.
|
||||
* @param {String} [oldServerName=""] - Old server name. This is a server name before any changes.
|
||||
* @param {Object} [columns={ "pc": "12", "tablet": "12", "mobile": "12" }] - Columns object.
|
||||
* @param {Array} [display=[]] - Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef.
|
||||
*/
|
||||
|
||||
const easyForm = useEasyForm();
|
||||
|
|
@ -97,8 +98,15 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: { pc: 12, tablet: 12, mobile: 12 },
|
||||
},
|
||||
display: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
const displayStore = useDisplayStore();
|
||||
|
||||
const data = reactive({
|
||||
base: JSON.parse(JSON.stringify(props.template)),
|
||||
currStep: 0,
|
||||
|
|
@ -109,8 +117,30 @@ const data = reactive({
|
|||
isReqErr: false,
|
||||
settingErr: "",
|
||||
stepErr: "",
|
||||
isDisplay: props.display.length
|
||||
? displayStore.isCurrentDisplay(props.display[0], props.display[1])
|
||||
: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @name checkDisplay
|
||||
* @description Check if the current display value is matching the display store value.
|
||||
* @returns {Void}
|
||||
*/
|
||||
function checkDisplay() {
|
||||
if (!props.display.length) return;
|
||||
data.isDisplay = displayStore.isCurrentDisplay(
|
||||
props.display[0],
|
||||
props.display[1]
|
||||
);
|
||||
}
|
||||
|
||||
// Case we have set a display group name and component id, the component id must match the current display id for the same group name to be displayed.
|
||||
if (props.display.length) {
|
||||
watch(displayStore.display, (val) => {
|
||||
checkDisplay();
|
||||
});
|
||||
}
|
||||
watch(
|
||||
() => props.template,
|
||||
() => {
|
||||
|
|
@ -202,6 +232,7 @@ onUnmounted(() => {
|
|||
|
||||
<template>
|
||||
<Container
|
||||
v-if="data.isDisplay"
|
||||
data-easy-form
|
||||
data-is="form"
|
||||
:tag="'form'"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Button from "@components/Widget/Button.vue";
|
|||
import Text from "@components/Widget/Text.vue";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useRawForm } from "@store/form.js";
|
||||
import { useDisplayStore } from "@store/global.js";
|
||||
|
||||
/**
|
||||
* @name Form/Raw.vue
|
||||
|
|
@ -27,6 +28,7 @@ import { useRawForm } from "@store/form.js";
|
|||
* @param {String} [endpoint=""] - Form endpoint. Case none, will be ignored.
|
||||
* @param {String} [method="POST"] - Http method to be used on form submit.
|
||||
* @param {Object} [columns={ "pc": "12", "tablet": "12", "mobile": "12" }] - Columns object.
|
||||
* @param {Array} [display=[]] - Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef.
|
||||
*/
|
||||
|
||||
const rawForm = useRawForm();
|
||||
|
|
@ -68,14 +70,44 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: { pc: 12, tablet: 12, mobile: 12 },
|
||||
},
|
||||
display: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
const displayStore = useDisplayStore();
|
||||
|
||||
const data = reactive({
|
||||
isValid: true,
|
||||
// This will be use to unmount and remount the editor (create a new editor instance because it is vanilla js)
|
||||
isRender: true,
|
||||
isDisplay: props.display.length
|
||||
? displayStore.isCurrentDisplay(props.display[0], props.display[1])
|
||||
: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @name checkDisplay
|
||||
* @description Check if the current display value is matching the display store value.
|
||||
* @returns {Void}
|
||||
*/
|
||||
function checkDisplay() {
|
||||
if (!props.display.length) return;
|
||||
data.isDisplay = displayStore.isCurrentDisplay(
|
||||
props.display[0],
|
||||
props.display[1]
|
||||
);
|
||||
}
|
||||
|
||||
// Case we have set a display group name and component id, the component id must match the current display id for the same group name to be displayed.
|
||||
if (props.display.length) {
|
||||
watch(displayStore.display, (val) => {
|
||||
checkDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.template,
|
||||
() => {
|
||||
|
|
@ -205,7 +237,7 @@ onMounted(() => {
|
|||
|
||||
<template>
|
||||
<Container
|
||||
data-raw-
|
||||
v-if="data.isDisplay"
|
||||
data-is="form"
|
||||
:tag="'form'"
|
||||
method="POST"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
<script setup>
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { defineProps, reactive, onMounted, watch } from "vue";
|
||||
import Container from "@components/Widget/Container.vue";
|
||||
import Fields from "@components/Form/Fields.vue";
|
||||
import ButtonGroup from "@components/Widget/ButtonGroup.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import { useDisplayStore } from "@store/global.js";
|
||||
|
||||
/**
|
||||
* @name Form/Regular.vue
|
||||
|
|
@ -40,6 +43,7 @@ import Text from "@components/Widget/Text.vue";
|
|||
* @param {String} [containerClass=""] - Container additional class
|
||||
* @param {String} [endpoint=""] - Form endpoint. Case none, will be ignored.
|
||||
* @param {String} [method="POST"] - Http method to be used on form submit.
|
||||
* @param {Array} [display=[]] - Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef.
|
||||
* @param {String} [maxWidthScreen="lg"] - Max screen width for the settings based on the breakpoint (xs, sm, md, lg, xl, 2xl)
|
||||
* @param {Object} [columns={ "pc": "12", "tablet": "12", "mobile": "12" }] - Columns object.
|
||||
*/
|
||||
|
|
@ -81,6 +85,11 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "POST",
|
||||
},
|
||||
display: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -93,6 +102,8 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const displayStore = useDisplayStore();
|
||||
|
||||
const data = reactive({
|
||||
base: props.fields,
|
||||
endpoint: props.endpoint,
|
||||
|
|
@ -101,8 +112,31 @@ const data = reactive({
|
|||
settingErr: "",
|
||||
pluginErr: "",
|
||||
isUpdateData: false,
|
||||
isDisplay: props.display.length
|
||||
? displayStore.isCurrentDisplay(props.display[0], props.display[1])
|
||||
: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @name checkDisplay
|
||||
* @description Check if the current display value is matching the display store value.
|
||||
* @returns {Void}
|
||||
*/
|
||||
function checkDisplay() {
|
||||
if (!props.display.length) return;
|
||||
data.isDisplay = displayStore.isCurrentDisplay(
|
||||
props.display[0],
|
||||
props.display[1]
|
||||
);
|
||||
}
|
||||
|
||||
// Case we have set a display group name and component id, the component id must match the current display id for the same group name to be displayed.
|
||||
if (props.display.length) {
|
||||
watch(displayStore.display, (val) => {
|
||||
checkDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
data.endpoint =
|
||||
window.location.origin + window.location.pathname + props.endpoint;
|
||||
|
|
@ -111,7 +145,8 @@ onMounted(() => {
|
|||
|
||||
<template>
|
||||
<Container
|
||||
data-is="form"
|
||||
v-if="data.isDisplay"
|
||||
data-is="form-regular"
|
||||
:tag="'form'"
|
||||
:method="props.method"
|
||||
:action="data.endpoint"
|
||||
|
|
@ -119,14 +154,19 @@ onMounted(() => {
|
|||
:containerClass="`form-regular-container`"
|
||||
>
|
||||
<div class="form-regular-wrap">
|
||||
<Title v-if="props.title" type="card" :title="props.title" />
|
||||
<Subtitle v-if="props.subtitle" type="card" :subtitle="props.subtitle" />
|
||||
<div
|
||||
:class="[
|
||||
'layout-settings-regular',
|
||||
`max-w-screen-${props.maxWidthScreen}`,
|
||||
]"
|
||||
>
|
||||
<Title v-if="props.title" type="card" :title="props.title" />
|
||||
<Subtitle
|
||||
v-if="props.subtitle"
|
||||
type="card"
|
||||
:subtitle="props.subtitle"
|
||||
/>
|
||||
|
||||
<template v-for="(field, key) in props.fields">
|
||||
<Fields :setting="field.setting" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { reactive, defineProps, computed, onBeforeMount } from "vue";
|
||||
import { reactive, defineProps, computed, onBeforeMount, watch } from "vue";
|
||||
import Container from "@components/Widget/Container.vue";
|
||||
import Grid from "@components/Widget/Grid.vue";
|
||||
import Select from "@components/Forms/Field/Select.vue";
|
||||
|
|
@ -8,6 +8,7 @@ import Advanced from "@components/Form/Advanced.vue";
|
|||
import Raw from "@components/Form/Raw.vue";
|
||||
import Easy from "@components/Form/Easy.vue";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useDisplayStore } from "@store/global.js";
|
||||
|
||||
/**
|
||||
* @name Form/Templates.vue
|
||||
|
|
@ -26,6 +27,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
* @param {Object} templates - List of advanced templates that contains settings. Must be a dict with mode as key, then the template name as key with a list of data (different for each modes).
|
||||
* @param {String} [operation="edit"] - Operation type (edit, new, delete).
|
||||
* @param {String} [oldServerName=""] - Old server name. This is a server name before any changes.
|
||||
* @param {Array} [display=[]] - Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef.
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -44,8 +46,15 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "",
|
||||
},
|
||||
display: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
const displayStore = useDisplayStore();
|
||||
|
||||
const comboboxTemplate = {
|
||||
id: `combobox-template-${uuidv4()}`,
|
||||
name: `combobox-template-${uuidv4()}`,
|
||||
|
|
@ -88,8 +97,30 @@ const data = reactive({
|
|||
if (!data.currModeName) return [];
|
||||
return Object.keys(props.templates[data.currModeName]);
|
||||
}),
|
||||
isDisplay: props.display.length
|
||||
? displayStore.isCurrentDisplay(props.display[0], props.display[1])
|
||||
: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @name checkDisplay
|
||||
* @description Check if the current display value is matching the display store value.
|
||||
* @returns {Void}
|
||||
*/
|
||||
function checkDisplay() {
|
||||
if (!props.display.length) return;
|
||||
data.isDisplay = displayStore.isCurrentDisplay(
|
||||
props.display[0],
|
||||
props.display[1]
|
||||
);
|
||||
}
|
||||
|
||||
// Case we have set a display group name and component id, the component id must match the current display id for the same group name to be displayed.
|
||||
if (props.display.length) {
|
||||
watch(displayStore.display, (val) => {
|
||||
checkDisplay();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @name getFirstTemplateName
|
||||
* @description Get the first template name from the first mode.
|
||||
|
|
@ -118,7 +149,7 @@ onBeforeMount(() => {
|
|||
|
||||
<template>
|
||||
<Container
|
||||
v-if="data.currModeName && data.currTemplateName"
|
||||
v-if="data.isDisplay && data.currModeName && data.currTemplateName"
|
||||
:containerClass="`col-span-12 w-full ${
|
||||
data.templates.length > 1 ? '' : 'mb-3'
|
||||
}`"
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { onMounted, reactive, ref } from "vue";
|
|||
* ],
|
||||
* }
|
||||
* @param {Array} buttons - List of buttons to display. Button component is used.
|
||||
* @param {String} [boutonGroupClass=""] - Additional class for the flex container
|
||||
* @param {String} [buttonGroupClass=""] - Additional class for the flex container
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -44,7 +44,7 @@ const props = defineProps({
|
|||
required: true,
|
||||
default: [],
|
||||
},
|
||||
boutonGroupClass: {
|
||||
buttonGroupClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
|
|
@ -59,7 +59,7 @@ const groupEl = ref(null);
|
|||
|
||||
onMounted(() => {
|
||||
group.class =
|
||||
props.boutonGroupClass || groupEl?.value?.closest("[data-is]")
|
||||
props.buttonGroupClass || groupEl?.value?.closest("[data-is]")
|
||||
? `button-group-${groupEl.value
|
||||
.closest("[data-is]")
|
||||
.getAttribute("data-is")}`
|
||||
|
|
@ -79,7 +79,7 @@ onMounted(() => {
|
|||
<template>
|
||||
<div
|
||||
ref="groupEl"
|
||||
:class="[group.class, props.boutonGroupClass]"
|
||||
:class="[group.class, props.buttonGroupClass]"
|
||||
v-if="props.buttons.length > 0"
|
||||
>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { useDisplayStore } from "@store/global.js";
|
|||
* @param {String} [gridLayoutClass="items-start"] - Additional class
|
||||
* @param {Array} [display=[]] - Array need two values : "groupName" in index 0 and "compId" in index 1 in order to be displayed using the display store. More info on the display store itslef.
|
||||
* @param {String} [tabId=contentIndex] - Case the container is converted to an anchor with a link, we can define the tabId, by default it is the contentIndex
|
||||
* @param {String} [maxWidthScreen="2xl"] - Max screen width for the settings based on the breakpoint (xs, sm, md, lg, xl, 2xl, 3xl)
|
||||
* @param {String} [maxWidthScreen="2xl"] - Max screen width for the settings based on the breakpoint (xs, sm, md, lg, xl, 2xl, 3xl)
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
|
|
|
|||
56
src/ui/client/dashboard/components/Widget/Image.vue
Normal file
56
src/ui/client/dashboard/components/Widget/Image.vue
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
|
||||
/**
|
||||
* @name Widget/Image.vue
|
||||
* @description This component is used for regular paragraph.
|
||||
* @example
|
||||
* {
|
||||
* src: "https://via.placeholder.com/150",
|
||||
* alt: "My image",
|
||||
* attrs: { id: "paragraph" },
|
||||
* }
|
||||
* @param {String} src - The src value of the image.
|
||||
* @param {String} [alt=""] - The alt value of the image. Can be a translation key or by default raw text.
|
||||
* @param {String} [imgClass=""]
|
||||
* @param {String} [imgContainerClass=""]
|
||||
* @param {Object} [attrs={}] - List of attributes to add to the image.
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
alt: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
imgClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "h-full w-full",
|
||||
},
|
||||
imgContainerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "flex justify-center items-center h-48 w-48",
|
||||
},
|
||||
attrs: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="props.src" :class="[props.imgContainerClass]">
|
||||
<img
|
||||
:alt="$t(props.alt, $t('dashboard_placeholder', props.alt))"
|
||||
v-bind="props.attrs"
|
||||
:class="[props.imgClass]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -13,7 +13,10 @@ import { onMounted, reactive, ref } from "vue";
|
|||
* }
|
||||
* @param {String} text - The text value. Can be a translation key or by default raw text.
|
||||
* @param {String} [textClass=""] - Style of text. Can be replace by any class starting by 'text-' like 'text-stat'.
|
||||
* @param {String} [textIconContainerClass="col-span-12 flex justify-center items-center"] - Case we have icon with text, we wrap the text on a container with the icon. We can add a class to this container.
|
||||
* @param {String} [color=""] - The color of the text between error, success, warning, info or tailwind color
|
||||
* @param {String} [iconName=""] - The name of the icon to display before the text.
|
||||
* @param {String} [iconColor=""] - The color of the icon.
|
||||
* @param {Boolean} [bold=false] - If the text should be bold or not.
|
||||
* @param {Boolean} [uppercase=false] - If the text should be uppercase or not.
|
||||
* @param {String} [tag="p"] - The tag of the text. Can be p, span, div, h1, h2, h3, h4, h5, h6
|
||||
|
|
@ -31,6 +34,11 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "",
|
||||
},
|
||||
textIconContainerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "col-span-12 flex justify-center items-center",
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -51,10 +59,15 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "p",
|
||||
},
|
||||
icon: {
|
||||
type: [Boolean, Object],
|
||||
iconName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: false,
|
||||
default: "",
|
||||
},
|
||||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
attrs: {
|
||||
type: Object,
|
||||
|
|
@ -75,15 +88,17 @@ onMounted(() => {
|
|||
// Check if next sibling is a
|
||||
const renderEl = textEl.value || textIconEl.value || null;
|
||||
text.class =
|
||||
props.textClass || renderEl.closest("[data-is]")
|
||||
? `text-${renderEl.closest("[data-is]").getAttribute("data-is")}`
|
||||
props.textClass || renderEl.closest("[data-is]:not([data-is='text'])")
|
||||
? `text-${renderEl
|
||||
.closest("[data-is]:not([data-is='text'])")
|
||||
.getAttribute("data-is")}`
|
||||
: "text-card";
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
v-if="!props.icon"
|
||||
v-if="!props.iconName"
|
||||
:is="props.tag"
|
||||
v-bind="props.attrs"
|
||||
ref="textEl"
|
||||
|
|
@ -98,8 +113,12 @@ onMounted(() => {
|
|||
{{ $t(props.text, $t("dashboard_placeholder", props.text)) }}
|
||||
</component>
|
||||
|
||||
<div :class="['flex justify-center items-center']" v-if="props.icon">
|
||||
<Icons v-if="props.icon" v-bind="props.icon" />
|
||||
<div
|
||||
:class="[props.textIconContainerClass]"
|
||||
v-if="props.iconName"
|
||||
data-is="text"
|
||||
>
|
||||
<Icons v-bind="{ iconName: props.iconName, color: props.iconColor }" />
|
||||
<component
|
||||
ref="textIconEl"
|
||||
:is="props.tag"
|
||||
|
|
|
|||
|
|
@ -414,5 +414,53 @@
|
|||
"configs_tab_add": "Add config",
|
||||
"configs_missing_types": "Impossible to retrieve config types",
|
||||
"configs_missing_services": "Impossible to retrieve services",
|
||||
"configs_not_found": "No configs found"
|
||||
"configs_not_found": "No configs found",
|
||||
"profile_info_not_found": "Impossible to retrieve profile information",
|
||||
"profile_info_title": "Information",
|
||||
"profile_info_subtitle": "Details about your account.",
|
||||
"profile_account_title": "Account",
|
||||
"profile_account_subtitle": "Manage some settings.",
|
||||
"profile_email": "Email",
|
||||
"profile_email_placeholder": "john.doe{'@'}gmail.com",
|
||||
"profile_email_desc": "Email is optional but recommended for password recovery or others features.",
|
||||
"profile_new_password": "New password",
|
||||
"profile_new_password_placeholder": "P{'@'}ssw0rd",
|
||||
"profile_new_password_desc": "Optional. Leave empty with confirm password to unchanged.",
|
||||
"profile_new_password_confirm": "New password confirm",
|
||||
"profile_new_password_confirm_placeholder": "P{'@'}ssw0rd",
|
||||
"profile_new_password_confirm_desc": "Optional. Leave empty with new password to unchanged.",
|
||||
"profile_current_password": "Current password",
|
||||
"profile_current_password_placeholder": "P{'@'}ssw0rd",
|
||||
"profile_current_password_desc": "This ensure you are the owner of the account.",
|
||||
"profile_recovery_codes_refresh_but_not_found": "Refresh done but no recovery codes found",
|
||||
"profile_refresh_recovery_codes": "Refresh recovery codes",
|
||||
"profile_totp_title": "TOTP",
|
||||
"profile_totp_subtitle": "Two-factor authentication.",
|
||||
"profile_totp_enable_state": "TOTP is currently enabled.",
|
||||
"profile_totp_disable_form_title": "Disable TOTP",
|
||||
"profile_totp_code": "TOTP code",
|
||||
"profile_totp_code_placeholder": "123456",
|
||||
"profile_totp_code_desc": "TOTP code is given by your authenticator app.",
|
||||
"profile_totp_disable_state": "TOTP is currently disabled.",
|
||||
"profile_totp_qr_code": "QR code to scan with your authenticator app.",
|
||||
"profile_totp_secret": "Secret key",
|
||||
"profile_totp_secret_placeholder": "secret",
|
||||
"profile_totp_secret_desc": "Another way to add TOTP to your authenticator app.",
|
||||
"profile_tab_profile": "Profile",
|
||||
"profile_tab_account": "Account",
|
||||
"profile_tab_totp": "TOTP",
|
||||
"profile_user_not_found": "Impossible to retrieve user information",
|
||||
"profile_totp_data_missing": "Impossible to retrieve TOTP data",
|
||||
"profile_username": "Username",
|
||||
"profile_created_method": "Created method",
|
||||
"profile_role": "Role",
|
||||
"profile_role_description": "Role description",
|
||||
"profile_permissions": "Permissions",
|
||||
"profile_creation_date": "Creation date",
|
||||
"profile_last_update": "Last update",
|
||||
"profile_totp_enable_form_title": "Enable TOTP",
|
||||
"profile_account_tab_email": "Email",
|
||||
"profile_account_tab_password": "Password",
|
||||
"profile_account_form_email_title": "Update email",
|
||||
"profile_account_form_password_title": "Update password"
|
||||
}
|
||||
|
|
|
|||
45
src/ui/client/dashboard/pages/profile/Profile.vue
Normal file
45
src/ui/client/dashboard/pages/profile/Profile.vue
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<script setup>
|
||||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderProfile from "@components/Builder/Profile.vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
import { useDisplayStore } from "@store/global.js";
|
||||
|
||||
/**
|
||||
* @name Page/Profile.vue
|
||||
* @description This component is the profile page.
|
||||
This page displays current profile and allows to manage them.
|
||||
We are using displayStore and setting ["main", 1] to display the profile list first.
|
||||
*/
|
||||
|
||||
// Set default store
|
||||
const displayStore = useDisplayStore();
|
||||
displayStore.setDisplay("main", 0);
|
||||
displayStore.setDisplay("account", 0);
|
||||
|
||||
const profile = reactive({
|
||||
builder: "",
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
// Get builder data
|
||||
const dataAtt = "data-server-builder";
|
||||
const dataEl = document.querySelector(`[${dataAtt}]`);
|
||||
const data =
|
||||
dataEl && !dataEl.getAttribute(dataAtt).includes(dataAtt)
|
||||
? JSON.parse(atob(dataEl.getAttribute(dataAtt)))
|
||||
: {};
|
||||
profile.builder = data;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// Set the page title
|
||||
useGlobal();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardLayout>
|
||||
<BuilderProfile v-if="profile.builder" :builder="profile.builder" />
|
||||
</DashboardLayout>
|
||||
</template>
|
||||
28
src/ui/client/dashboard/pages/profile/index.html
Normal file
28
src/ui/client/dashboard/pages/profile/index.html
Normal file
File diff suppressed because one or more lines are too long
11
src/ui/client/dashboard/pages/profile/profile.js
Normal file
11
src/ui/client/dashboard/pages/profile/profile.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import { getI18n } from "@utils/lang.js";
|
||||
import Profile from "./Profile.vue";
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
createApp(Profile)
|
||||
.use(pinia)
|
||||
.use(getI18n(["dashboard", "action", "inp", "icons", "profile"]))
|
||||
.mount("#app");
|
||||
|
|
@ -616,7 +616,7 @@ body {
|
|||
}
|
||||
|
||||
.layout-settings-regular {
|
||||
@apply py-4 gap-y-4 sm:gap-x-4 md:gap-x-6 col-span-12 grid grid-cols-12 w-full relative;
|
||||
@apply pt-2 pb-4 gap-y-4 sm:gap-x-4 md:gap-x-6 col-span-12 grid grid-cols-12 w-full relative;
|
||||
}
|
||||
|
||||
.layout-settings-table {
|
||||
|
|
@ -644,6 +644,10 @@ body {
|
|||
@apply col-span-12 w-full mx-2 my-4;
|
||||
}
|
||||
|
||||
.msg-unmatch-form-regular {
|
||||
@apply col-span-12 w-full mx-2 my-4;
|
||||
}
|
||||
|
||||
.msg-unmatch-base {
|
||||
@apply col-span-12 w-full mx-2 my-4;
|
||||
}
|
||||
|
|
@ -714,6 +718,10 @@ body {
|
|||
@apply block w-8 h-8 pointer-events-none;
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
@apply block w-6 h-6 pointer-events-none;
|
||||
}
|
||||
|
||||
.icon-menu {
|
||||
@apply h-6 w-6 relative pointer-events-none;
|
||||
}
|
||||
|
|
@ -722,10 +730,14 @@ body {
|
|||
@apply h-6 w-6 relative pointer-events-none;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
.icon-form-regular {
|
||||
@apply h-6 w-6 relative pointer-events-none;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
@apply h-6 w-6 relative pointer-events-none -translate-y-0.4;
|
||||
}
|
||||
|
||||
.icon-table-content {
|
||||
@apply h-6.5 w-6.5 relative pointer-events-none;
|
||||
}
|
||||
|
|
@ -1192,7 +1204,7 @@ body {
|
|||
}
|
||||
|
||||
.text-card {
|
||||
@apply col-span-12 text-lg mb-0 dark:text-gray-300 break-word;
|
||||
@apply col-span-12 text-base mb-0 dark:text-gray-300 break-word;
|
||||
}
|
||||
|
||||
.text-unmatch {
|
||||
|
|
@ -1229,6 +1241,10 @@ body {
|
|||
@apply col-span-12 flex justify-center items-center;
|
||||
}
|
||||
|
||||
.button-group-form-regular {
|
||||
@apply col-span-12 flex justify-center items-center;
|
||||
}
|
||||
|
||||
.button-group-tabs {
|
||||
@apply col-span-12 flex flex-col sm:flex-row justify-center items-center gap-2;
|
||||
}
|
||||
|
|
@ -1259,7 +1275,7 @@ body {
|
|||
/* LIST COMPONENT */
|
||||
|
||||
.list-pairs-container {
|
||||
@apply col-span-12 grid grid-cols-12 gap-2 my-4;
|
||||
@apply col-span-12 grid grid-cols-12 gap-3 my-4;
|
||||
}
|
||||
|
||||
.list-pairs-item {
|
||||
|
|
@ -1324,6 +1340,10 @@ body {
|
|||
@apply break-word w-full max-w-[80%] sm:max-w-[800px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-xl text-[#344767] tracking-normal;
|
||||
}
|
||||
|
||||
.title-form-regular {
|
||||
@apply break-word w-full flex justify-center max-w-[80%] sm:max-w-[800px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-xl text-[#344767] tracking-normal;
|
||||
}
|
||||
|
||||
.title-content {
|
||||
@apply break-word w-full max-w-[80%] sm:max-w-[700px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-lg text-[#344767];
|
||||
}
|
||||
|
|
@ -1356,11 +1376,16 @@ body {
|
|||
@apply mb-2;
|
||||
}
|
||||
|
||||
.no-subtitle.title-form-regular {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
.no-subtitle.title-stat {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
.is-subtitle.title-form,
|
||||
.is-subtitle.title-form-regular,
|
||||
.is-subtitle.title-card,
|
||||
.is-subtitle.title-void,
|
||||
.is-subtitle.title-modal,
|
||||
|
|
@ -1388,6 +1413,10 @@ body {
|
|||
@apply break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[800px] leading-normal text-[1.05rem] mb-0;
|
||||
}
|
||||
|
||||
.subtitle-form-regular {
|
||||
@apply break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[800px] leading-normal text-[1.05rem] mb-0;
|
||||
}
|
||||
|
||||
.subtitle-content {
|
||||
@apply break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-[0.975rem] mb-0;
|
||||
}
|
||||
|
|
@ -1485,7 +1514,7 @@ body {
|
|||
/* BTN */
|
||||
|
||||
.btn {
|
||||
@apply w-full flex justify-center items-end tracking-wide dark:brightness-90 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
|
||||
@apply w-full flex justify-center items-center tracking-wide dark:brightness-90 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
|
||||
}
|
||||
|
||||
.btn.active:not([disabled]) {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -300,6 +300,18 @@ export default {
|
|||
"sm:mr-4",
|
||||
"md:mr-4",
|
||||
"lg:mr-4",
|
||||
"my-0",
|
||||
"sm:my-0",
|
||||
"md:my-0",
|
||||
"lg:my-0",
|
||||
"my-2",
|
||||
"sm:my-2",
|
||||
"md:my-2",
|
||||
"lg:my-2",
|
||||
"my-4",
|
||||
"sm:my-4",
|
||||
"md:my-4",
|
||||
"lg:my-4",
|
||||
"pl-2",
|
||||
"sm:pl-2",
|
||||
"md:pl-2",
|
||||
|
|
@ -324,6 +336,18 @@ export default {
|
|||
"sm:px-2",
|
||||
"md:px-2",
|
||||
"lg:px-2",
|
||||
"px-4",
|
||||
"sm:px-4",
|
||||
"md:px-4",
|
||||
"lg:px-4",
|
||||
"py-2",
|
||||
"sm:py-2",
|
||||
"md:py-2",
|
||||
"lg:py-2",
|
||||
"py-4",
|
||||
"sm:py-4",
|
||||
"md:py-4",
|
||||
"lg:py-4",
|
||||
"scale-90",
|
||||
"scale-95",
|
||||
"w-fit",
|
||||
|
|
|
|||
Loading…
Reference in a new issue