Compare commits
2 Commits
i18n
...
aff1586c9b
| Author | SHA1 | Date | |
|---|---|---|---|
| aff1586c9b | |||
| 567c2c0853 |
@@ -5,9 +5,8 @@ from uuid import UUID
|
||||
|
||||
from beanie import PydanticObjectId
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic.json_schema import SkipJsonSchema
|
||||
|
||||
from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry, ForeignKey
|
||||
from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry
|
||||
from firm.core.filter import Filter, FilterSchema
|
||||
from firm.entity.models import Entity
|
||||
|
||||
@@ -26,10 +25,27 @@ class ContractDraftStatus(str, Enum):
|
||||
|
||||
|
||||
class DraftParty(BaseModel):
|
||||
entity_id: PydanticObjectId = ForeignKey("entities", "Entity", default="", title="Partie")
|
||||
entity: SkipJsonSchema[Entity] = Field(default=None, exclude=True, )
|
||||
entity_id: PydanticObjectId = Field(
|
||||
foreignKey={
|
||||
"reference": {
|
||||
"resource": "entities",
|
||||
"schema": "Entity",
|
||||
}
|
||||
},
|
||||
default="",
|
||||
title="Partie"
|
||||
)
|
||||
part: str = Field(title="Rôle")
|
||||
representative_id: PydanticObjectId = ForeignKey("entities", "Entity", default="", title="Représentant")
|
||||
representative_id: PydanticObjectId = Field(
|
||||
foreignKey={
|
||||
"reference": {
|
||||
"resource": "entities",
|
||||
"schema": "Entity",
|
||||
}
|
||||
},
|
||||
default="",
|
||||
title="Représentant"
|
||||
)
|
||||
|
||||
class Config:
|
||||
title = 'Partie'
|
||||
@@ -58,10 +74,14 @@ class ProvisionGenuine(BaseModel):
|
||||
|
||||
class ContractProvisionTemplateReference(BaseModel):
|
||||
type: Literal['template'] = ContractProvisionType.template
|
||||
provision_template_id: PydanticObjectId = ForeignKey(
|
||||
"templates/provisions",
|
||||
"ProvisionTemplate",
|
||||
displayed_fields=['title', 'body'],
|
||||
provision_template_id: PydanticObjectId = Field(
|
||||
foreignKey={
|
||||
"reference": {
|
||||
"resource": "templates/provisions",
|
||||
"schema": "ProvisionTemplate",
|
||||
"displayedFields": ['title', 'body']
|
||||
},
|
||||
},
|
||||
props={"parametrized": True},
|
||||
default="",
|
||||
title="Template de clause"
|
||||
@@ -153,9 +173,6 @@ class ContractDraft(CrudDocument):
|
||||
update = ContractDraftUpdateStatus(status=status)
|
||||
await self.update(db, self, update)
|
||||
|
||||
def compute_label(self) -> str:
|
||||
return f"{self.name} - {self.title}"
|
||||
|
||||
class Contract(CrudDocument):
|
||||
"""
|
||||
Contrat publié. Les contrats ne peuvent pas être modifiés.
|
||||
|
||||
@@ -114,20 +114,6 @@ def RichtextSingleline(*args, **kwargs):
|
||||
return Field(*args, **kwargs)
|
||||
|
||||
|
||||
def ForeignKey(resource, schema, displayed_fields=None, *args, **kwargs):
|
||||
kwargs["foreignKey"] = {
|
||||
"reference": {
|
||||
"resource": resource,
|
||||
"schema": schema,
|
||||
}
|
||||
}
|
||||
|
||||
if displayed_fields:
|
||||
kwargs["foreignKey"]["reference"]["displayedFields"] = displayed_fields
|
||||
|
||||
return Field(*args, **kwargs)
|
||||
|
||||
|
||||
class DictionaryEntry(BaseModel):
|
||||
key: str
|
||||
value: str = ""
|
||||
|
||||
@@ -32,19 +32,6 @@ services:
|
||||
- "traefik.http.routers.gui.rule=PathPrefix(`/`)"
|
||||
- "traefik.http.services.gui.loadbalancer.server.port=5173"
|
||||
|
||||
i18n:
|
||||
build:
|
||||
context: ./i18n
|
||||
restart: always
|
||||
volumes:
|
||||
- ./i18n/app/src:/app/src
|
||||
- ./gui/rpk-gui/public:/app/public
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.i18n.entrypoints=web"
|
||||
- "traefik.http.routers.i18n.rule=PathPrefix(`/locales/add`)"
|
||||
- "traefik.http.services.i18n.loadbalancer.server.port=8100"
|
||||
|
||||
proxy:
|
||||
image: traefik:latest
|
||||
restart: always
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
{
|
||||
"pages": {
|
||||
"login": {
|
||||
"title": "Melden Sie sich bei Ihrem Konto an",
|
||||
"signin": "Einloggen",
|
||||
"signup": "Anmelden",
|
||||
"divider": "oder",
|
||||
"fields": {
|
||||
"email": "Email",
|
||||
"password": "Passwort"
|
||||
},
|
||||
"oauth": {
|
||||
"google": "Einloggen mit Google",
|
||||
"discord": "Einloggen mit Discord"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Ungültige E-Mail-Adresse",
|
||||
"requiredEmail": "E-Mail ist erforderlich",
|
||||
"requiredPassword": "Passwort wird benötigt"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Anmeldung",
|
||||
"forgotPassword": "Passwort vergessen?",
|
||||
"noAccount": "Sie haben kein Konto?",
|
||||
"rememberMe": "Erinnere dich an mich"
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"title": "Haben Sie Ihr Passwort vergessen?",
|
||||
"fields": {
|
||||
"email": "Email"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Ungültige E-Mail-Adresse",
|
||||
"requiredEmail": "E-Mail ist erforderlich"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Anweisungen zum Zurücksetzen senden"
|
||||
}
|
||||
},
|
||||
"register": {
|
||||
"title": "Registrieren Sie sich für Ihr Konto",
|
||||
"fields": {
|
||||
"email": "Email",
|
||||
"password": "Passwort"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Ungültige E-Mail-Adresse",
|
||||
"requiredEmail": "E-Mail ist erforderlich",
|
||||
"requiredPassword": "Passwort wird benötigt"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Registrieren",
|
||||
"haveAccount": "Ein Konto haben?"
|
||||
}
|
||||
},
|
||||
"updatePassword": {
|
||||
"title": "Kennwort aktualisieren",
|
||||
"fields": {
|
||||
"password": "Neues Passwort",
|
||||
"confirmPassword": "Bestätige neues Passwort"
|
||||
},
|
||||
"errors": {
|
||||
"confirmPasswordNotMatch": "Passwörter stimmen nicht überein",
|
||||
"requiredPassword": "Passwort wird benötigt",
|
||||
"requiredConfirmPassword": "Das Feld „Passwort bestätigen“ ist erforderlich"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Aktualisieren"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"info": "Sie haben vergessen, {{action}} component zu {{resource}} hinzufügen.",
|
||||
"404": "Leider existiert diese Seite nicht.",
|
||||
"resource404": "Haben Sie die {{resource}} resource erstellt?",
|
||||
"backHome": "Zurück"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"list": "Aufführen",
|
||||
"create": "Erstellen",
|
||||
"edit": "Bearbeiten",
|
||||
"show": "Zeigen"
|
||||
},
|
||||
"buttons": {
|
||||
"create": "Erstellen",
|
||||
"save": "Speichern",
|
||||
"logout": "Abmelden",
|
||||
"delete": "Löschen",
|
||||
"edit": "Bearbeiten",
|
||||
"cancel": "Abbrechen",
|
||||
"confirm": "Sicher?",
|
||||
"filter": "Filter",
|
||||
"clear": "Löschen",
|
||||
"refresh": "Erneuern",
|
||||
"show": "Zeigen",
|
||||
"undo": "Undo",
|
||||
"import": "Importieren",
|
||||
"clone": "Klon",
|
||||
"notAccessTitle": "Sie haben keine zugriffsberechtigung"
|
||||
},
|
||||
"warnWhenUnsavedChanges": "Nicht gespeicherte Änderungen werden nicht übernommen.",
|
||||
"notifications": {
|
||||
"success": "Erfolg",
|
||||
"error": "Fehler (status code: {{statusCode}})",
|
||||
"undoable": "Sie haben {{seconds}} Sekunden Zeit für Undo.",
|
||||
"createSuccess": "{{resource}} erfolgreich erstellt.",
|
||||
"createError": "Fehler beim Erstellen {{resource}} (status code: {{statusCode}})",
|
||||
"deleteSuccess": "{{resource}} erfolgreich gelöscht.",
|
||||
"deleteError": "Fehler beim Löschen {{resource}} (status code: {{statusCode}})",
|
||||
"editSuccess": "{{resource}} erfolgreich bearbeitet.",
|
||||
"editError": "Fehler beim Bearbeiten {{resource}} (status code: {{statusCode}})",
|
||||
"importProgress": "{{processed}}/{{total}} importiert"
|
||||
},
|
||||
"loading": "Wird geladen",
|
||||
"tags": {
|
||||
"clone": "Klon"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard"
|
||||
},
|
||||
"posts": {
|
||||
"posts": "Einträge",
|
||||
"fields": {
|
||||
"id": "Id",
|
||||
"title": "Titel",
|
||||
"category": "Kategorie",
|
||||
"status": {
|
||||
"title": "Status",
|
||||
"published": "Veröffentlicht",
|
||||
"draft": "Draft",
|
||||
"rejected": "Abgelehnt"
|
||||
},
|
||||
"content": "Inhalh",
|
||||
"createdAt": "Erstellt am"
|
||||
},
|
||||
"titles": {
|
||||
"create": "Erstellen",
|
||||
"edit": "Bearbeiten",
|
||||
"list": "Einträge",
|
||||
"show": "Eintrag zeigen"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"actions": "Aktionen"
|
||||
},
|
||||
"documentTitle": {
|
||||
"default": "refine",
|
||||
"suffix": " | Refine",
|
||||
"post": {
|
||||
"list": "Beiträge | Refine",
|
||||
"show": "#{{id}} Beitrag anzeigen | Refine",
|
||||
"edit": "#{{id}} Beitrag bearbeiten | Refine",
|
||||
"create": "Neuen Beitrag erstellen | Refine",
|
||||
"clone": "#{{id}} Beitrag klonen | Refine"
|
||||
}
|
||||
},
|
||||
"autoSave": {
|
||||
"success": "gespeichert",
|
||||
"error": "fehler beim automatischen speichern",
|
||||
"loading": "speichern...",
|
||||
"idle": "warten auf anderungen"
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
{
|
||||
"pages": {
|
||||
"home": {
|
||||
"title": "Home"
|
||||
},
|
||||
"login": {
|
||||
"title": "Sign in to your account",
|
||||
"signin": "Sign in",
|
||||
"signup": "Sign up",
|
||||
"divider": "or",
|
||||
"fields": {
|
||||
"email": "Email",
|
||||
"password": "Password"
|
||||
},
|
||||
"oauth": {
|
||||
"google": "Sign in with Google",
|
||||
"discord": "Sign in with Discord"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Invalid email address",
|
||||
"requiredEmail": "Email is required",
|
||||
"requiredPassword": "Password is required"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Login",
|
||||
"forgotPassword": "Forgot password?",
|
||||
"noAccount": "Don’t have an account?",
|
||||
"rememberMe": "Remember me"
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"title": "Forgot your password?",
|
||||
"fields": {
|
||||
"email": "Email"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Invalid email address",
|
||||
"requiredEmail": "Email is required"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Send reset instructions"
|
||||
}
|
||||
},
|
||||
"register": {
|
||||
"title": "Sign up for your account",
|
||||
"fields": {
|
||||
"email": "Email",
|
||||
"password": "Password"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Invalid email address",
|
||||
"requiredEmail": "Email is required",
|
||||
"requiredPassword": "Password is required"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Register",
|
||||
"haveAccount": "Have an account?"
|
||||
}
|
||||
},
|
||||
"updatePassword": {
|
||||
"title": "Update password",
|
||||
"fields": {
|
||||
"password": "New Password",
|
||||
"confirmPassword": "Confirm new password"
|
||||
},
|
||||
"errors": {
|
||||
"confirmPasswordNotMatch": "Passwords do not match",
|
||||
"requiredPassword": "Password required",
|
||||
"requiredConfirmPassword": "Confirm password is required"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Update"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"404": "Sorry, the page you visited does not exist.",
|
||||
"info": "You may have forgotten to add the {{action}} component to {{resource}} resource.",
|
||||
"resource404": "Are you sure you have created the {{resource}} resource.",
|
||||
"backHome": "Back Home"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"list": "List",
|
||||
"create": "Create",
|
||||
"edit": "Edit",
|
||||
"show": "Show"
|
||||
},
|
||||
"buttons": {
|
||||
"create": "Create",
|
||||
"save": "Save",
|
||||
"logout": "Logout",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Are you sure?",
|
||||
"filter": "Filter",
|
||||
"clear": "Clear",
|
||||
"refresh": "Refresh",
|
||||
"show": "Show",
|
||||
"undo": "Undo",
|
||||
"import": "Import",
|
||||
"clone": "Clone",
|
||||
"notAccessTitle": "You don't have permission to access"
|
||||
},
|
||||
"warnWhenUnsavedChanges": "Are you sure you want to leave? You have unsaved changes.",
|
||||
"notifications": {
|
||||
"success": "Successful",
|
||||
"error": "Error (status code: {{statusCode}})",
|
||||
"undoable": "You have {{seconds}} seconds to undo",
|
||||
"createSuccess": "Successfully created {{resource}}",
|
||||
"createError": "There was an error creating {{resource}} (status code: {{statusCode}})",
|
||||
"deleteSuccess": "Successfully deleted {{resource}}",
|
||||
"deleteError": "Error when deleting {{resource}} (status code: {{statusCode}})",
|
||||
"editSuccess": "Successfully edited {{resource}}",
|
||||
"editError": "Error when editing {{resource}} (status code: {{statusCode}})",
|
||||
"importProgress": "Importing: {{processed}}/{{total}}"
|
||||
},
|
||||
"loading": "Loading",
|
||||
"tags": {
|
||||
"clone": "Clone"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard"
|
||||
},
|
||||
"posts": {
|
||||
"posts": "Posts",
|
||||
"fields": {
|
||||
"id": "Id",
|
||||
"title": "Title",
|
||||
"category": "Category",
|
||||
"status": {
|
||||
"title": "Status",
|
||||
"published": "Published",
|
||||
"draft": "Draft",
|
||||
"rejected": "Rejected"
|
||||
},
|
||||
"content": "Content",
|
||||
"createdAt": "Created At"
|
||||
},
|
||||
"titles": {
|
||||
"create": "Create Post",
|
||||
"edit": "Edit Post",
|
||||
"list": "Posts",
|
||||
"show": "Show Post"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"actions": "Actions"
|
||||
},
|
||||
"documentTitle": {
|
||||
"default": "refine",
|
||||
"suffix": " | Refine",
|
||||
"post": {
|
||||
"list": "Posts | Refine",
|
||||
"show": "#{{id}} Show Post | Refine",
|
||||
"edit": "#{{id}} Edit Post | Refine",
|
||||
"create": "Create new Post | Refine",
|
||||
"clone": "#{{id}} Clone Post | Refine"
|
||||
}
|
||||
},
|
||||
"autoSave": {
|
||||
"success": "saved",
|
||||
"error": "auto save failure",
|
||||
"loading": "saving...",
|
||||
"idle": "waiting for changes"
|
||||
},
|
||||
"undefined": {
|
||||
"undefined": "No translation",
|
||||
"titles": {
|
||||
"list": "No translation"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"individual": {
|
||||
"type": "Individual",
|
||||
"lastname": "Lastname",
|
||||
"surnames": "Surname",
|
||||
"day_of_birth": "Date of birth",
|
||||
"firstname": "Firstname",
|
||||
"place_of_birth": "Place of birth",
|
||||
"middlename": "Middlename",
|
||||
"resource_title": "Individual"
|
||||
},
|
||||
"corporation": {
|
||||
"type": "Corporation",
|
||||
"activity": "Activity",
|
||||
"title": "Title",
|
||||
"employees": "Employees",
|
||||
"resource_title": "Corporation"
|
||||
},
|
||||
"employee": {
|
||||
"position": "Position",
|
||||
"entity_id": "Identity",
|
||||
"resource_title": "Employee"
|
||||
},
|
||||
"institution": {
|
||||
"type": "Institution",
|
||||
"title": "Title",
|
||||
"activity": "Activity",
|
||||
"employees": "Employees",
|
||||
"resource_title": "Institution"
|
||||
},
|
||||
"entity": {
|
||||
"entity_data": "Informations",
|
||||
"address": "Address",
|
||||
"resource_title": "Entity"
|
||||
},
|
||||
"provision_template": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"body": "Body",
|
||||
"resource_title": "Provision Template"
|
||||
},
|
||||
"contract_template": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"provisions": "Provisions",
|
||||
"parties": "Parties",
|
||||
"variables": "Variables",
|
||||
"resource_title": "Contract Template"
|
||||
},
|
||||
"party_template": {
|
||||
"entity_id": "Party Template",
|
||||
"representative_id": "Representative",
|
||||
"part": "Part",
|
||||
"resource_title": "Party"
|
||||
},
|
||||
"provision_template_reference": {
|
||||
"provision_template_id": "Provision Template",
|
||||
"resource_title": "Provision Template"
|
||||
},
|
||||
"dictionary_entry": {
|
||||
"key": "Variable",
|
||||
"value": "Value",
|
||||
"resource_title": "Variables"
|
||||
},
|
||||
"contract_draft": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"parties": "Parties",
|
||||
"provisions": "Provisions",
|
||||
"variables": "Variables",
|
||||
"resource_title": "Contract Draft"
|
||||
},
|
||||
"draft_party": {
|
||||
"entity_id": "Client",
|
||||
"part": "Part",
|
||||
"representative_id": "Representative",
|
||||
"resource_title": "Party"
|
||||
},
|
||||
"contract_provision_template_reference": {
|
||||
"provision_template_id": "Provision Template",
|
||||
"type": "Provision Template",
|
||||
"resource_title": "Provision Template"
|
||||
},
|
||||
"provision_genuine": {
|
||||
"title": "Title",
|
||||
"body": "Body",
|
||||
"type": "Genuine Provision",
|
||||
"resource_title": "Genuine Provision"
|
||||
},
|
||||
"draft_provision": {
|
||||
"provision": "Provision",
|
||||
"resource_title": "Provision"
|
||||
},
|
||||
"contract": {
|
||||
"date": "Date",
|
||||
"location": "Location",
|
||||
"resource_title": "Contract",
|
||||
"draft_id": "Draft"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
{
|
||||
"pages": {
|
||||
"home": {
|
||||
"title": "Page d'accueil"
|
||||
},
|
||||
"login": {
|
||||
"title": "Authentification",
|
||||
"signin": "S'authentifier",
|
||||
"signup": "Créer un compte",
|
||||
"divider": "ou",
|
||||
"fields": {
|
||||
"email": "Email",
|
||||
"password": "Mot de passe"
|
||||
},
|
||||
"oauth": {
|
||||
"google": "S'authentifier avec Google",
|
||||
"discord": "S'authentifier avec Discord"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Email invalide",
|
||||
"requiredEmail": "l'Email est obligatoire",
|
||||
"requiredPassword": "Le mot de passe est obligatoire"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "S'authentifier",
|
||||
"forgotPassword": "Mot de passe oublié?",
|
||||
"noAccount": "Vous n'avec pas de compte?",
|
||||
"rememberMe": "Se souvenir de moi"
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"title": "Mot de passe oublié?",
|
||||
"fields": {
|
||||
"email": "Email"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "mail invalide",
|
||||
"requiredEmail": "l'Email est obligatoire"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Envoyer les instructions de récupération"
|
||||
}
|
||||
},
|
||||
"register": {
|
||||
"title": "Création de compte",
|
||||
"fields": {
|
||||
"email": "Email",
|
||||
"password": "Mot de passe"
|
||||
},
|
||||
"errors": {
|
||||
"validEmail": "Email invalide",
|
||||
"requiredEmail": "l'Email est obligatoire",
|
||||
"requiredPassword": "Le mot de passe est obligatoire"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Créer un compte",
|
||||
"haveAccount": "Vous avez déjà un compte?"
|
||||
}
|
||||
},
|
||||
"updatePassword": {
|
||||
"title": "Mise à jour du mot de passe",
|
||||
"fields": {
|
||||
"password": "Nouveau mot de passe",
|
||||
"confirmPassword": "Confirmation"
|
||||
},
|
||||
"errors": {
|
||||
"confirmPasswordNotMatch": "Les mots de passe ne correspondent pas",
|
||||
"requiredPassword": "Le mot de passe est obligatoire",
|
||||
"requiredConfirmPassword": "Vous devez confirmer votre mot de passe"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Mettre à jour"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"404": "Cette page n'existe pas.",
|
||||
"info": "Il manque l'action {{action}} component à la ressource {{resource}} .",
|
||||
"resource404": "Cette page n'existe pas.",
|
||||
"backHome": "Retour à l'accueil"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"list": "Liste",
|
||||
"create": "Création",
|
||||
"edit": "Édtion",
|
||||
"show": "Voir"
|
||||
},
|
||||
"buttons": {
|
||||
"create": "Créer",
|
||||
"save": "Sauvegarder",
|
||||
"logout": "Déconnexion",
|
||||
"delete": "Supprimer",
|
||||
"edit": "Modifier",
|
||||
"cancel": "Annuler",
|
||||
"confirm": "Êtes vous sur?",
|
||||
"filter": "Filtrer",
|
||||
"clear": "Effacer",
|
||||
"refresh": "Rafraîchir",
|
||||
"show": "Voir",
|
||||
"undo": "Annuler",
|
||||
"import": "Importer",
|
||||
"clone": "Cloner",
|
||||
"notAccessTitle": "Vous n'avez pas la permission d'accéder à cette ressource"
|
||||
},
|
||||
"warnWhenUnsavedChanges": "Êtes vous sur de vouloir quitter la page? Vous avez des modification non sauvegardées.",
|
||||
"notifications": {
|
||||
"success": "Succès",
|
||||
"error": "Erreur (Code de statut: {{statusCode}})",
|
||||
"undoable": "Vous avez {{seconds}} secondes à annuler",
|
||||
"createSuccess": "Création de {{resource}} réussie",
|
||||
"createError": "Erreur pendant la création de {{resource}} (Code de statut: {{statusCode}})",
|
||||
"deleteSuccess": "Suppression de {{resource}} réussie",
|
||||
"deleteError": "Erreur pendant la suppression de {{resource}} (Code de statut: {{statusCode}})",
|
||||
"editSuccess": "Modification de {{resource}} réussie",
|
||||
"editError": "Erreur pendant la modification de {{resource}} (Code de statut: {{statusCode}})",
|
||||
"importProgress": "Importation de: {{processed}}/{{total}}"
|
||||
},
|
||||
"loading": "Chargement",
|
||||
"tags": {
|
||||
"clone": "Clone"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Tableau de bord"
|
||||
},
|
||||
"posts": {
|
||||
"posts": "Posts",
|
||||
"fields": {
|
||||
"id": "Id",
|
||||
"title": "Title",
|
||||
"category": "Category",
|
||||
"status": {
|
||||
"title": "Status",
|
||||
"published": "Published",
|
||||
"draft": "Draft",
|
||||
"rejected": "Rejected"
|
||||
},
|
||||
"content": "Content",
|
||||
"createdAt": "Created At"
|
||||
},
|
||||
"titles": {
|
||||
"create": "Create Post",
|
||||
"edit": "Edit Post",
|
||||
"list": "Posts",
|
||||
"show": "Show Post"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"actions": "Actions"
|
||||
},
|
||||
"documentTitle": {
|
||||
"default": "refine",
|
||||
"suffix": " | Refine",
|
||||
"post": {
|
||||
"list": "Posts | Refine",
|
||||
"show": "#{{id}} Show Post | Refine",
|
||||
"edit": "#{{id}} Edit Post | Refine",
|
||||
"create": "Create new Post | Refine",
|
||||
"clone": "#{{id}} Clone Post | Refine"
|
||||
}
|
||||
},
|
||||
"autoSave": {
|
||||
"success": "Sauvegardé",
|
||||
"error": "Sauvegarde automatique ratée",
|
||||
"loading": "Sauvegarde...",
|
||||
"idle": "En attente de modification"
|
||||
},
|
||||
"undefined": {
|
||||
"undefined": "No translation",
|
||||
"titles": {
|
||||
"list": "No translation"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"individual": {
|
||||
"type": "Particulier",
|
||||
"middlename": "Autres prénoms",
|
||||
"lastname": "Nom",
|
||||
"firstname": "Prénom",
|
||||
"day_of_birth": "Date de naissance",
|
||||
"surnames": "Surnoms",
|
||||
"place_of_birth": "Lieu de naissance",
|
||||
"resource_title": "Particulier"
|
||||
},
|
||||
"corporation": {
|
||||
"type": "Entreprise",
|
||||
"title": "Titre",
|
||||
"activity": "Activité",
|
||||
"employees": "Employés",
|
||||
"resource_title": "Entreprise"
|
||||
},
|
||||
"employee": {
|
||||
"entity_id": "Identité",
|
||||
"position": "Poste",
|
||||
"resource_title": "Employé"
|
||||
},
|
||||
"institution": {
|
||||
"type": "Institution",
|
||||
"title": "Titre",
|
||||
"employees": "Employés",
|
||||
"activity": "Activité",
|
||||
"resource_title": "Institution"
|
||||
},
|
||||
"entity": {
|
||||
"entity_data": "Informations",
|
||||
"address": "Adresse",
|
||||
"resource_title": "Entité"
|
||||
},
|
||||
"provision_template": {
|
||||
"name": "Nom",
|
||||
"body": "Corps",
|
||||
"title": "Titre",
|
||||
"resource_title": "Template de Clause"
|
||||
},
|
||||
"contract_template": {
|
||||
"name": "Nom",
|
||||
"title": "Titre",
|
||||
"parties": "Parties",
|
||||
"provisions": "Clauses",
|
||||
"variables": "Variables",
|
||||
"resource_title": "Template de Contrat"
|
||||
},
|
||||
"party_template": {
|
||||
"entity_id": "Entité",
|
||||
"part": "Rôle",
|
||||
"representative_id": "Représentant",
|
||||
"resource_title": "Partie"
|
||||
},
|
||||
"provision_template_reference": {
|
||||
"provision_template_id": "Template de clause",
|
||||
"resource_title": "Template de clause"
|
||||
},
|
||||
"dictionary_entry": {
|
||||
"key": "Variable",
|
||||
"value": "Valeur",
|
||||
"resource_title": "Variables"
|
||||
},
|
||||
"contract_draft": {
|
||||
"name": "Nom",
|
||||
"parties": "Parties",
|
||||
"title": "Titre",
|
||||
"provisions": "Clauses",
|
||||
"variables": "Variables",
|
||||
"resource_title": "Brouillon de Contrat"
|
||||
},
|
||||
"draft_party": {
|
||||
"part": "Rôle",
|
||||
"representative_id": "Représentant",
|
||||
"entity_id": "Entité",
|
||||
"resource_title": "Partie"
|
||||
},
|
||||
"contract_provision_template_reference": {
|
||||
"type": "Template",
|
||||
"provision_template_id": "Template de clause",
|
||||
"resource_title": "Template de clause"
|
||||
},
|
||||
"provision_genuine": {
|
||||
"type": "Personalisée",
|
||||
"title": "Titre",
|
||||
"body": "Corps",
|
||||
"resource_title": "Clause personnalisée"
|
||||
},
|
||||
"draft_provision": {
|
||||
"provision": "Clause",
|
||||
"resource_title": "Clause"
|
||||
},
|
||||
"contract": {
|
||||
"draft_id": "Brouillon",
|
||||
"resource_title": "Contrat",
|
||||
"location": "Lieu",
|
||||
"date": "Date"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
gui/rpk-gui/public/locales/en/common.json
Normal file
7
gui/rpk-gui/public/locales/en/common.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"pages": {
|
||||
"login": {
|
||||
"title": "Sign in to your account"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
gui/rpk-gui/public/locales/fr/common.json
Normal file
7
gui/rpk-gui/public/locales/fr/common.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"pages": {
|
||||
"login": {
|
||||
"title": "S'authentifier"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { FirmRoutes } from "./pages/firm";
|
||||
import rpcTheme from "./theme";
|
||||
|
||||
function App() {
|
||||
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const i18nProvider: I18nProvider = {
|
||||
@@ -77,7 +78,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route
|
||||
element={(
|
||||
<Authenticated key="authenticated-routes" redirectOnFail="/login" fallback={<CatchAllNavigate to="/login"/>}>
|
||||
<Authenticated key="authenticated-routes" redirectOnFail="/auth/login" fallback={<CatchAllNavigate to="/auth/login"/>}>
|
||||
<Outlet />
|
||||
</Authenticated>
|
||||
)}
|
||||
@@ -85,13 +86,13 @@ function App() {
|
||||
<Route path="hub/*" element={<HubRoutes />} />
|
||||
<Route path="firm/*" element={<FirmRoutes />} />
|
||||
</Route>
|
||||
<Route path="*" element={<Outlet />}>
|
||||
<Route path="auth/*" element={<Outlet />}>
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route path="register" element={<Register />} />
|
||||
<Route path="forgot-password" element={<ForgotPassword />} />
|
||||
<Route path="update-password" element={<UpdatePassword />} />
|
||||
</Route>
|
||||
<Route index element={<><Header /><h1>{t("pages.home.title")}</h1></>} />
|
||||
<Route index element={<><Header /><h1>HOME</h1></>} />
|
||||
</Routes>
|
||||
<UnsavedChangesNotifier />
|
||||
<DocumentTitleHandler />
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { useSearchParams, Navigate } from "react-router";
|
||||
import { useTranslation } from "@refinedev/core";
|
||||
import { AuthPage } from "@refinedev/mui";
|
||||
|
||||
import GoogleIcon from "@mui/icons-material/Google";
|
||||
import DiscordIcon from "../../components/DiscordIcon";
|
||||
import {useSearchParams, Navigate, Link} from "react-router";
|
||||
import MuiLink from "@mui/material/Link";
|
||||
import * as React from "react";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
|
||||
export const Login = () => {
|
||||
const { translate } = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
if (searchParams.get("oauth") == "success") {
|
||||
const redirect_to = localStorage.getItem("redirect_after_login")
|
||||
@@ -20,15 +24,67 @@ export const Login = () => {
|
||||
rememberMe={false}
|
||||
providers={[{
|
||||
name: "google",
|
||||
label: translate("pages.login.oauth.google"),
|
||||
label: "Sign in with Google",
|
||||
icon: (<GoogleIcon style={{ fontSize: 24, }} />),
|
||||
},
|
||||
{
|
||||
name: "discord",
|
||||
label: translate("pages.login.oauth.discord"),
|
||||
label: "Sign in with Discord",
|
||||
icon: (<DiscordIcon style={{ fontSize: 24, }} />),
|
||||
},
|
||||
]}
|
||||
forgotPasswordLink={
|
||||
<Stack
|
||||
sx={{
|
||||
direction: "row",
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<MuiLink
|
||||
variant="body2"
|
||||
color="primary"
|
||||
fontSize="12px"
|
||||
component={Link}
|
||||
underline="none"
|
||||
to="/auth/forgot-password"
|
||||
>
|
||||
Forgot password?
|
||||
</MuiLink>
|
||||
</Stack>
|
||||
}
|
||||
registerLink={
|
||||
<Box
|
||||
sx={{
|
||||
mt: "24px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
textAlign="center"
|
||||
variant="body2"
|
||||
component="span"
|
||||
fontSize="12px"
|
||||
>
|
||||
Don’t have an account?
|
||||
</Typography>
|
||||
<MuiLink
|
||||
ml="4px"
|
||||
fontSize="12px"
|
||||
variant="body2"
|
||||
color="primary"
|
||||
component={Link}
|
||||
underline="none"
|
||||
to="/auth/register"
|
||||
fontWeight="bold"
|
||||
>
|
||||
Sign up
|
||||
</MuiLink>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Button } from "@mui/material";
|
||||
import { useLogout } from "@refinedev/core";
|
||||
import { useTranslation } from "@refinedev/core";
|
||||
|
||||
export const Logout = () => {
|
||||
const { translate } = useTranslation();
|
||||
const { mutate: logout } = useLogout();
|
||||
|
||||
return <Button onClick={() => logout()} >{ translate("buttons.logout") }</Button>;
|
||||
return <Button onClick={() => logout()} >Logout</Button>;
|
||||
};
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Box from "@mui/material/Box";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation as useRefineTranslation } from "@refinedev/core";
|
||||
|
||||
const I18nPicker = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const { getLocale, changeLocale } = useRefineTranslation();
|
||||
const currentLocale = getLocale();
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
value={currentLocale}
|
||||
options={i18n.languages}
|
||||
disableClearable={true}
|
||||
renderInput={(params) => {
|
||||
return <TextField {...params} label={ "Language" } variant="outlined" />
|
||||
}}
|
||||
renderOption={(props, option) => {
|
||||
const { key, ...optionProps } = props;
|
||||
return (
|
||||
<Box
|
||||
key={key}
|
||||
component="li"
|
||||
sx={{ '& > img': { mr: 2, flexShrink: 0 } }}
|
||||
{...optionProps}
|
||||
>
|
||||
{ option }
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
onChange={(event, value) => {
|
||||
changeLocale(value);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default I18nPicker;
|
||||
@@ -18,11 +18,17 @@ import { FirmContext } from "../../contexts/FirmContext";
|
||||
import { Logout } from "../auth/Logout";
|
||||
import { IUser } from "../../interfaces";
|
||||
import MuiLink from "@mui/material/Link";
|
||||
import I18nPicker from "./I18nPicker";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation as useTranslationR } from "@refinedev/core";
|
||||
import { useSetLocale } from "@refinedev/core";
|
||||
|
||||
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
||||
sticky = true,
|
||||
}) => {
|
||||
const { i18n } = useTranslation();
|
||||
const { getLocale, changeLocale } = useTranslationR();
|
||||
const currentLocale = getLocale();
|
||||
|
||||
const collapsed = false;
|
||||
const { mode, setMode } = useContext(ColorModeContext);
|
||||
const { currentFirm } = useContext(FirmContext);
|
||||
@@ -38,6 +44,16 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const [anchorIn, setAnchorIn] = React.useState<null | HTMLElement>(null);
|
||||
const openI18nMenu = Boolean(anchorEl);
|
||||
const handleOpenI18nMenu = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorIn(event.currentTarget);
|
||||
}
|
||||
const handleCloseI18nMenu = () => {
|
||||
setAnchorIn(null);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<AppBar position={sticky ? "sticky" : "relative"}>
|
||||
<Toolbar>
|
||||
@@ -131,9 +147,45 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
||||
</Stack>
|
||||
)}
|
||||
{!user && (
|
||||
<Link to="/login"><Button>Login</Button></Link>
|
||||
<Link to="/auth/login"><Button>Login</Button></Link>
|
||||
)}
|
||||
<I18nPicker />
|
||||
<Button
|
||||
id="i18n-button"
|
||||
aria-controls={openI18nMenu ? 'i18n-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={openI18nMenu ? 'true' : undefined}
|
||||
onClick={handleOpenI18nMenu}>
|
||||
<Typography
|
||||
sx={{
|
||||
display: {
|
||||
xs: "none",
|
||||
sm: "inline-block",
|
||||
},
|
||||
}}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{currentLocale}
|
||||
</Typography>
|
||||
<Avatar src={`/images/flags/${currentLocale}.svg`} alt={currentLocale}/>
|
||||
</Button>
|
||||
<Menu
|
||||
id="i18n-menu"
|
||||
open={openI18nMenu}
|
||||
anchorEl={anchorIn}
|
||||
onClose={handleCloseI18nMenu}
|
||||
>
|
||||
{[...(i18n.languages || [])].sort().map((lang: string) => (
|
||||
<MenuItem
|
||||
key={lang}
|
||||
onClick={() => changeLocale(lang)}
|
||||
>
|
||||
<span style={{ marginRight: 8 }}>
|
||||
<Avatar src={`/images/flags/${lang}.svg`} alt={lang}/>
|
||||
</span>
|
||||
{lang === "en" ? "English" : "Français"}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Toolbar>
|
||||
|
||||
@@ -8,14 +8,13 @@ i18n
|
||||
.use(detector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
supportedLngs: ["EN", "FR"],
|
||||
supportedLngs: ["en", "fr"],
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json", // "http/locales/{{lng}}/{{ns}}.json"
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json", // locale files path
|
||||
},
|
||||
//saveMissing: true,
|
||||
ns: ["common"],
|
||||
defaultNS: "common",
|
||||
fallbackLng: ["EN", "FR"],
|
||||
fallbackLng: ["en", "fr"],
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -4,7 +4,6 @@ import { RegistryFieldsType, RegistryWidgetsType, RJSFSchema, UiSchema } from "@
|
||||
import CrudTextWidget from "./widgets/crud-text-widget";
|
||||
import UnionEnumField from "./fields/union-enum";
|
||||
import { ResourceContext } from "../contexts/ResourceContext";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type BaseFormProps = {
|
||||
schema: RJSFSchema,
|
||||
@@ -13,7 +12,6 @@ type BaseFormProps = {
|
||||
onChange?: (data: any) => void,
|
||||
uiSchema?: UiSchema,
|
||||
formData?: any,
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
export const customWidgets: RegistryWidgetsType = {
|
||||
@@ -25,7 +23,7 @@ export const customFields: RegistryFieldsType = {
|
||||
}
|
||||
|
||||
export const BaseForm: React.FC<BaseFormProps> = (props) => {
|
||||
const { schema, uiSchema, resourceBasePath, formData, children, onSubmit, onChange } = props;
|
||||
const { schema, uiSchema, resourceBasePath, formData, onSubmit, onChange } = props;
|
||||
|
||||
return (
|
||||
<ResourceContext.Provider value={{basePath: resourceBasePath}} >
|
||||
@@ -39,7 +37,6 @@ export const BaseForm: React.FC<BaseFormProps> = (props) => {
|
||||
widgets={customWidgets}
|
||||
fields={customFields}
|
||||
onChange={(e, id) => onChange != undefined && onChange(e.formData)}
|
||||
children={children}
|
||||
/>
|
||||
</ResourceContext.Provider>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CircularProgress } from "@mui/material";
|
||||
import { useForm } from "@refinedev/core";
|
||||
import { UiSchema } from "@rjsf/utils";
|
||||
@@ -12,12 +12,11 @@ type CrudFormProps = {
|
||||
resource: string,
|
||||
id?: string,
|
||||
onSuccess?: (data: any) => void,
|
||||
defaultValue?: any,
|
||||
children?: ReactNode
|
||||
defaultValue?: any
|
||||
}
|
||||
|
||||
export const CrudForm: React.FC<CrudFormProps> = (props) => {
|
||||
const { schemaName, uiSchema, resourceBasePath="" ,resource, id, onSuccess, defaultValue, children } = props;
|
||||
const { schemaName, uiSchema, resourceBasePath="" ,resource, id, onSuccess, defaultValue } = props;
|
||||
|
||||
const { onFinish, query, formLoading } = useForm({
|
||||
resource: resourceBasePath == "" ? resource : `${resourceBasePath}/${resource}`,
|
||||
@@ -58,7 +57,6 @@ export const CrudForm: React.FC<CrudFormProps> = (props) => {
|
||||
onSubmit={
|
||||
(data: any) => onFinish(data)
|
||||
}
|
||||
children={children}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { JSONSchema7Definition } from "json-schema";
|
||||
import { RJSFSchema } from '@rjsf/utils';
|
||||
import i18n from '../../../i18n'
|
||||
|
||||
const API_URL = "/api/v1";
|
||||
|
||||
@@ -19,14 +19,9 @@ const getJsonschema = async (): Promise<RJSFSchema> => {
|
||||
return rawSchema;
|
||||
}
|
||||
|
||||
function convertCamelToSnake(str: string): string {
|
||||
return str.replace(/([a-zA-Z])(?=[A-Z])/g,'$1_').toLowerCase()
|
||||
}
|
||||
|
||||
function buildResource(rawSchemas: RJSFSchema, resourceName: string) {
|
||||
let resource;
|
||||
|
||||
const shortResourceName = convertCamelToSnake(resourceName.replace(/(-Input|Create|Update)$/g, ""));
|
||||
resource = structuredClone(rawSchemas.components.schemas[resourceName]);
|
||||
resource.components = { schemas: {} };
|
||||
for (let prop_name in resource.properties) {
|
||||
@@ -50,13 +45,6 @@ function buildResource(rawSchemas: RJSFSchema, resourceName: string) {
|
||||
} else if (is_array(prop) && is_reference(prop.items)) {
|
||||
resolveReference(rawSchemas, resource, prop.items);
|
||||
}
|
||||
|
||||
if (prop.hasOwnProperty("title")) {
|
||||
prop.title = i18n.t(`schemas.${shortResourceName}.${convertCamelToSnake(prop_name)}`, prop.title);
|
||||
}
|
||||
}
|
||||
if (resource.hasOwnProperty("title")) {
|
||||
resource.title = i18n.t(`schemas.${shortResourceName}.resource_title`, resource.title);
|
||||
}
|
||||
|
||||
return resource;
|
||||
@@ -173,6 +161,7 @@ function path_exists(rawSchemas: RJSFSchema, resource: RJSFSchema, path: string)
|
||||
return has_descendant(rawSchemas, resource, path);
|
||||
}
|
||||
|
||||
|
||||
return has_descendant(rawSchemas, resource, path.substring(0, pointFirstPosition))
|
||||
&& path_exists(
|
||||
rawSchemas,
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { Route, Routes } from "react-router";
|
||||
import { CircularProgress } from "@mui/material";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { useOne } from "@refinedev/core";
|
||||
import { BaseForm } from "../../lib/crud/components/base-form";
|
||||
import { ForeignKeyReference, ForeignKeySchema } from "../../lib/crud/components/widgets/foreign-key";
|
||||
|
||||
import { FirmContext } from "../../contexts/FirmContext";
|
||||
import List from "./base-page/List";
|
||||
import Edit from "./base-page/Edit";
|
||||
import New from "./base-page/New";
|
||||
@@ -36,79 +29,6 @@ const EditDraft = () => {
|
||||
return <Edit<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} />
|
||||
}
|
||||
|
||||
type ForeignKeySubSchema = ForeignKeySchema & {
|
||||
properties: { [key: string]: { foreignKey: { reference: ForeignKeyReference } } }
|
||||
}
|
||||
|
||||
const CreateDraft = () => {
|
||||
const [chosenDraft, setChosenDraft] = useState<string|null>(null)
|
||||
const { currentFirm } = useContext(FirmContext);
|
||||
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
|
||||
const templateFieldSchema: ForeignKeySubSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
template_id: {
|
||||
type: "string",
|
||||
title: "Find a template",
|
||||
foreignKey: {
|
||||
reference: {
|
||||
resource: "templates/contracts",
|
||||
schema: "ContractTemplate"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const templateForm = (
|
||||
<BaseForm
|
||||
schema={templateFieldSchema}
|
||||
formData={{template_id: chosenDraft}}
|
||||
resourceBasePath={resourceBasePath}
|
||||
onChange={(data) => {
|
||||
const { template_id } = data;
|
||||
setChosenDraft(template_id);
|
||||
}}
|
||||
>
|
||||
|
||||
</BaseForm>
|
||||
)
|
||||
|
||||
if (chosenDraft !== null) {
|
||||
return (
|
||||
<>
|
||||
{templateForm}
|
||||
<CreateDraftFromTemplate template_id={chosenDraft}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{templateForm}
|
||||
<New<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const CreateDraftFromTemplate = (props: { template_id: string }) => {
|
||||
const { template_id } = props;
|
||||
const { currentFirm } = useContext(FirmContext);
|
||||
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
|
||||
const resource = "templates/contracts"
|
||||
const { data, isLoading } = useOne({
|
||||
resource: `${resourceBasePath}/${resource}`,
|
||||
id: template_id
|
||||
});
|
||||
|
||||
if (isLoading || data === undefined) {
|
||||
return <CircularProgress />
|
||||
}
|
||||
|
||||
let template = { ...data.data };
|
||||
template.provisions = data.data.provisions.map((item: any) => {
|
||||
return { provision: {type: "template", provision_template_id: item.provision_template_id} }
|
||||
})
|
||||
|
||||
return <New<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} defaultValue={ template }/>
|
||||
return <New<Draft> resource={`contracts/drafts`} schemaName={"ContractDraft"} />
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { CrudForm } from "../../../lib/crud/components/crud-form";
|
||||
import { UiSchema } from "@rjsf/utils";
|
||||
import { useParams } from "react-router";
|
||||
import { useContext } from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import { FirmContext } from "../../../contexts/FirmContext";
|
||||
import { CrudForm } from "../../../lib/crud/components/crud-form";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { DeleteButton } from "@refinedev/mui";
|
||||
|
||||
type EditProps = {
|
||||
resource: string,
|
||||
@@ -22,26 +17,13 @@ const Edit = <T,>(props: EditProps) => {
|
||||
const { record_id } = useParams();
|
||||
|
||||
return (
|
||||
<>
|
||||
<CrudForm
|
||||
schemaName={schemaName}
|
||||
uiSchema={uiSchema}
|
||||
resourceBasePath={resourceBasePath}
|
||||
resource={resource}
|
||||
id={record_id}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<Button type='submit' variant="contained" size="large"><SaveIcon />Save</Button>
|
||||
<DeleteButton variant="contained" size="large" color="error" recordItemId={record_id}/>
|
||||
</Stack>
|
||||
</CrudForm>
|
||||
</>
|
||||
<CrudForm
|
||||
schemaName={schemaName}
|
||||
uiSchema={uiSchema}
|
||||
resourceBasePath={resourceBasePath}
|
||||
resource={resource}
|
||||
id={record_id}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ const authProvider: AuthProvider = {
|
||||
if (get_user() == null) {
|
||||
return {
|
||||
authenticated: false,
|
||||
redirectTo: "/login",
|
||||
redirectTo: "/auth/login",
|
||||
logout: true
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ const authProvider: AuthProvider = {
|
||||
if (error?.status === 401) {
|
||||
forget_user();
|
||||
return {
|
||||
redirectTo: "/login",
|
||||
redirectTo: "/auth/login",
|
||||
logout: true,
|
||||
error: { message: "Authentication required" },
|
||||
} as OnErrorResponse;
|
||||
|
||||
@@ -96,7 +96,7 @@ const dataProvider: DataProvider = {
|
||||
};
|
||||
},
|
||||
create: async ({ resource, variables }) => {
|
||||
const response = await fetch(`${API_URL}/${resource}/`, {
|
||||
const response = await fetch(`${API_URL}/${resource}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(variables),
|
||||
headers: {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
FROM node:lts-alpine
|
||||
|
||||
WORKDIR /app
|
||||
COPY app/package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY app/tsconfig*.json ./
|
||||
COPY app/src ./src
|
||||
EXPOSE 8100
|
||||
|
||||
CMD [ "npm", "--watch", "start" ]
|
||||
26
i18n/app/.gitignore
vendored
26
i18n/app/.gitignore
vendored
@@ -1,26 +0,0 @@
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
\*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
||||
.vscode/_
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
_.suo
|
||||
_.ntvs_
|
||||
_.njsproj
|
||||
_.sln
|
||||
\*.sw?
|
||||
1164
i18n/app/package-lock.json
generated
1164
i18n/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "i18n Helper",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node src/index.ts",
|
||||
"build": "tsc",
|
||||
"serve": "node dist/index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/node": "^22.15.3",
|
||||
"express": "^5.1.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^2.2.0"
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import bodyParser from "body-parser";
|
||||
|
||||
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
|
||||
|
||||
|
||||
|
||||
const app = express();
|
||||
app.use(bodyParser.json());
|
||||
const port = process.env.PORT || 8100;
|
||||
|
||||
app.post('/locales/add/:lng/:ns', (req: Request, res: Response) => {
|
||||
const keyPath = Object.keys(req.body)[0];
|
||||
|
||||
const basePath = "public/locales";
|
||||
const lgPath = `${basePath}/${req.params.lng}`;
|
||||
if (!existsSync(lgPath)) {
|
||||
mkdirSync(lgPath);
|
||||
}
|
||||
const filePath = `${lgPath}/common.json`;
|
||||
|
||||
let missingTrans;
|
||||
try {
|
||||
missingTrans = JSON.parse(readFileSync(filePath, 'utf8'));
|
||||
} catch(err) {
|
||||
missingTrans = JSON.parse("{}");
|
||||
}
|
||||
|
||||
let current = missingTrans
|
||||
const splitPath = keyPath.split(".");
|
||||
for (let i=0; i < splitPath.length; i++) {
|
||||
const key = splitPath[i];
|
||||
if (! current.hasOwnProperty(key)) {
|
||||
if (i + 1 == splitPath.length) {
|
||||
current[key] = "No translation";
|
||||
} else {
|
||||
current[key] = JSON.parse("{}");
|
||||
}
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
writeFileSync(filePath, JSON.stringify(missingTrans, null, 2))
|
||||
|
||||
res.send("OK");
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}`);
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user