This commit is contained in:
Chen Lily 2024-01-17 17:31:41 +08:00
parent 84190ed9f1
commit fb9e920c15
35 changed files with 3003 additions and 1304 deletions

211
language/en-au/res.json Normal file
View File

@ -0,0 +1,211 @@
{
"ver": "#{$X-Res-Ver}",
"title": "Fleet Intelligence\u2122 Internal",
"logout": "Logout",
"workOrder": "Work Orders You Are Following",
"periodCol": "Period:",
"openedDateCol": "Opened Date:",
"closedDateCol": "Closed Date:",
"to": "to",
"threeMonths": "Within three months",
"thisYear": "This year",
"lastYear": "Last Year",
"all": "All",
"allOpened": "All Opened",
"thisMonth": "This Month",
"lastMonth": "Last Month",
"thisQuarter": "This Quarter",
"lastQuarter": "Last Quarter",
"yearToDate": "Year To Date",
"custom": "Custom",
"refresh": "Refresh",
"exportExcel": "Export to Excel",
"report": "Report",
"allItem": "( All )",
"ok": "OK",
"reset": "Reset",
"yes": "Yes",
"no": "No",
"unknownError": "An unknown error occurred, please contact the administrator.",
"searchHolder": "Search...",
"workOrderNumber": "Work Order Number",
"workOrderNumberWidth": 170,
"workOrderType": "Work Order Type",
"workOrderTypeWidth": 190,
"assignedTo": "Assigned To",
"assignedToWidth": 190,
"dealerName": "Dealer Name",
"dealerNameWidth": 270,
"status": "Status",
"statusWidth": 180,
"createDate": "Created Date",
"createDateWidth": 130,
"completeDate": "Completed Date",
"completeDateWidth": 130,
"completed": "Completed",
"totalCost": "Total Cost",
"totalCostWidth": 100,
"vin": "VIN",
"vinWidth": 150,
"make": "Make",
"makeColon": "Make: ",
"makeWidth": 150,
"model": "Model",
"modelColon": "Model:",
"modelWidth": 150,
"type": "Type",
"typeWidth": 150,
"customerCode": "Customer Code",
"customerWidth": 180,
"userProfile": "User Profile",
"save": "Save",
"cancel": "Cancel",
"userName": "Display Name",
"mobilePhone": "Mobile Phone",
"timeZone": "Time Zone",
"language": "Language",
"companyName": "Company Name",
"companyNameWidth": 270,
"advisor": "Advisor",
"advisorWidth": 120,
"lastCommunicationText": "Last Communication Text",
"lastCommunicationTextWidth": 320,
"lastCommunicationBy": "Last Communication By",
"lastCommunicationByWidth": 190,
"lastCommunicationDate": "Last Communication Date",
"lastCommunicationDateWidth": 190,
"displayClosed": "Display Closed (90 days)",
"companyNameColon": "Company Name:",
"woNumberColon": "WO#:",
"savedSuccessfully": "Saved successfully.",
"viewWorkOrder": "Work Order - {0}",
"informationonAssetColon": "Information on Asset: ",
"workOrderColon": "Work Order: ",
"statusColon": "Status:",
"billingInformation": "Billing Information",
"estimateTotalAmountColon": "Estimate Total Amount: ",
"notesColon": "Notes: ",
"issuesIdentifiedinInspection": "Issues Identified in Inspection",
"identifiedIssues": "IDENTIFIED ISSUES",
"inspectionDetail": "INSPECTION DETAIL",
"noIdentifieds": "No Issues Identified in Inspection",
"severityLevelColon": "Severity Level: ",
"low": "Low",
"medium": "Medium",
"high": "High",
"noQuestions": "No Questions",
"workOrderAttachments": "Work Order Attachments",
"messages": "Customer Communication",
"internalComments": "Internal Comments",
"sentTo": "Sent To :",
"sent": "Sent",
"undelivered": "Undelivered",
"delivered": "Delivered",
"failed": "Failed",
"review": "Review",
"pending": "Pending",
"optOut": "Opt-Out",
"landline": "Landline",
"confirmSendMessage": "Message will be sent to:",
"confirmContinue": "Do you want to continue?",
"send": "Send",
"viewWorkOrderInspection": "View Work Order Inspection - {0}",
"viewFullInspection": "View Full Inspection",
"summary": "Summary",
"welcomeTitle": "Thank you for your business!",
"workOrderNumberColon": "Work Order Number:",
"openDateColon": "Open Date:",
"completedDateColon": "Completed Date:",
"approveRejectEstimate": "Approve/Reject Estimate",
"partsCostColon": "Parts Cost:",
"laborCostColon": "Labor Cost:",
"travelTimeCostColon": "Travel Time Cost:",
"otherCostColon": "Other Cost:",
"assetDetail": "Asset Detail",
"assetColon": "Asset:",
"hourMeterColon": "Hour Meter:",
"vinColon": "VIN:",
"typeColon": "Type:",
"odometerColon": "Odometer:",
"workOrderDetail": "Work Order Detail",
"viewApproveEstimate": "View/Approve Estimate",
"workOrderTypeColon": "Work Order Type:",
"customerReportedIssuesColon": "Issues:",
"workOrderTotalCostColon": "Work Order Total Cost:",
"estimateTotalCostColon": "Estimate Total Cost:",
"timeToCompleteColon": "Time To Complete(Hrs):",
"estimateDetail": "Estimate Detail",
"notesFromCustomerColon": "Notes from Customer:",
"approve": "Approve",
"reject": "Reject",
"approveEstimate": "Approve Estimate",
"rejectEstimate": "Reject Estimate",
"confirmApproveEstimate": "Are you sure you want to approve the estimate?",
"confirmRejectEstimate": "Are you sure you want to reject the estimate?",
"estimateStatusColon": "Estimate Status:",
"technicianNotesColon": "Technician Notes:",
"draft": "Draft",
"awaitingResponse": "Awaiting Response",
"closedByDealer": "Closed By Dealer",
"customerRejected": "Customer Rejected",
"customerApproved": "Customer Approved",
"estimateAttachments": "Estimate Attachments",
"photoDocuments": "Photos and Documentation",
"estimateNotPrepared": "Estimate Not Prepared",
"notAvailable": "N/A",
"signatureColon": "Signature:",
"signature": "Signature",
"requireSignature": "Please make a signature first.",
"clear": "Clear",
"approvedByColon": "Approved By:",
"phoneNumberColon": "Phone Number:",
"requireApprovedBy": "Please input the approver.",
"requirePhoneNumber": "Please input the phone number.",
"willWorkOurselves": "Will perform work ourselves",
"willWorkElsewhere": "Will have work done elsewhere",
"cost": "Cost",
"willNotWork": "Will not have work done",
"willWorkLater": "Will have work done at a later date",
"other": "Other",
"preferNotAnswer": "Prefer Not to Answer",
"reasonColon": "Reason:",
"termOfUse": "Terms of Use",
"requireReason": "Please select or input a reason.",
"submit": "Submit",
"survey": "Survey",
"thanksSurvey": "Your survey has been submitted successfully. Thank you for taking the survey.",
"surveyPrompt": "Please Rate the services that you received on work order {wonum} for your {make}/{model}",
"invoiceDetail": "Invoice Detail",
"awaitingPayment": "Awaiting Payment",
"paymentFailure": "Payment Failure",
"customerPaid": "Paid",
"invoiceStatusColon": "Invoice Status:",
"invoiceTotalCostColon": "Invoice Total Cost:",
"taxesColon": "Taxes:",
"invoiceNotPrepared": "Invoice Not Prepared",
"makePayment": "Make Payment",
"typeMessage": "Type your message here",
"typeComment": "Enter Message Here",
"poNumberColon": "PO Number:",
"requirePONumber": "Please Enter a PO Number.",
"void": "Void",
"nameColon": "Name:",
"requireCustomer": "Please select one customer contact at least.",
"openInApp": "Open In App",
"layout": "Layout",
"resetLayout": "Reset Layout",
"resetPivots": "Reset Pivots",
"confirmResetLayout": "Are you sure you want to reset the layout?",
"selectColumns": "Select Columns",
"column": "Column",
"caption": "Caption",
"postNote": "Post Note",
"postedByColon": "Posted by:",
"emailedToColon": "Emailed to:",
"sendMessage": "Send Message",
"editContacts": "Edit Contacts",
"autoUpdateEnabled": "Auto Updates Enabled",
"autoUpdateDisabled": "Auto Updates Disabled",
"statusLinkIncluded": "Status Link Included",
"statusLinkExcluded": "Status Link Excluded"
}

211
language/en/res.json Normal file
View File

@ -0,0 +1,211 @@
{
"ver": "#{$X-Res-Ver}",
"title": "Fleet Intelligence\u2122 Internal",
"logout": "Logout",
"workOrder": "Work Orders You Are Following",
"periodCol": "Period:",
"openedDateCol": "Opened Date:",
"closedDateCol": "Closed Date:",
"to": "to",
"threeMonths": "Within three months",
"thisYear": "This year",
"lastYear": "Last Year",
"all": "All",
"allOpened": "All Opened",
"thisMonth": "This Month",
"lastMonth": "Last Month",
"thisQuarter": "This Quarter",
"lastQuarter": "Last Quarter",
"yearToDate": "Year To Date",
"custom": "Custom",
"refresh": "Refresh",
"exportExcel": "Export to Excel",
"report": "Report",
"allItem": "( All )",
"ok": "OK",
"reset": "Reset",
"yes": "Yes",
"no": "No",
"unknownError": "An unknown error occurred, please contact the administrator.",
"searchHolder": "Search...",
"workOrderNumber": "Work Order Number",
"workOrderNumberWidth": 170,
"workOrderType": "Work Order Type",
"workOrderTypeWidth": 190,
"assignedTo": "Assigned To",
"assignedToWidth": 190,
"dealerName": "Dealer Name",
"dealerNameWidth": 270,
"status": "Status",
"statusWidth": 180,
"createDate": "Created Date",
"createDateWidth": 130,
"completeDate": "Completed Date",
"completeDateWidth": 130,
"completed": "Completed",
"totalCost": "Total Cost",
"totalCostWidth": 100,
"vin": "VIN",
"vinWidth": 150,
"make": "Make",
"makeColon": "Make: ",
"makeWidth": 150,
"model": "Model",
"modelColon": "Model: ",
"modelWidth": 150,
"type": "Type",
"typeWidth": 150,
"customerCode": "Customer Code",
"customerWidth": 180,
"userProfile": "User Profile",
"save": "Save",
"cancel": "Cancel",
"userName": "Display Name",
"mobilePhone": "Mobile Phone",
"timeZone": "Time Zone",
"language": "Language",
"companyName": "Company Name",
"companyNameWidth": 270,
"advisor": "Advisor",
"advisorWidth": 120,
"lastCommunicationText": "Last Communication Text",
"lastCommunicationTextWidth": 320,
"lastCommunicationBy": "Last Communication By",
"lastCommunicationByWidth": 190,
"lastCommunicationDate": "Last Communication Date",
"lastCommunicationDateWidth": 190,
"displayClosed": "Display Closed (90 days)",
"companyNameColon": "Company Name:",
"woNumberColon": "WO#:",
"savedSuccessfully": "Saved successfully.",
"viewWorkOrder": "Work Order - {0}",
"informationonAssetColon": "Information on Asset: ",
"workOrderColon": "Work Order: ",
"statusColon": "Status:",
"billingInformation": "Billing Information",
"estimateTotalAmountColon": "Estimate Total Amount: ",
"notesColon": "Notes: ",
"issuesIdentifiedinInspection": "Issues Identified in Inspection",
"identifiedIssues": "IDENTIFIED ISSUES",
"inspectionDetail": "INSPECTION DETAIL",
"noIdentifieds": "No Issues Identified in Inspection",
"severityLevelColon": "Severity Level: ",
"low": "Low",
"medium": "Medium",
"high": "High",
"noQuestions": "No Questions",
"workOrderAttachments": "Work Order Attachments",
"messages": "Customer Communication",
"internalComments": "Internal Comments",
"sentTo": "Sent To :",
"sent": "Sent",
"undelivered": "Undelivered",
"delivered": "Delivered",
"failed": "Failed",
"review": "Review",
"pending": "Pending",
"optOut": "Opt-Out",
"landline": "Landline",
"confirmSendMessage": "Message will be sent to:",
"confirmContinue": "Do you want to continue?",
"send": "Send",
"viewWorkOrderInspection": "View Work Order Inspection - {0}",
"viewFullInspection": "View Full Inspection",
"summary": "Summary",
"welcomeTitle": "Thank you for your business!",
"workOrderNumberColon": "Work Order Number:",
"openDateColon": "Open Date:",
"completedDateColon": "Completed Date:",
"approveRejectEstimate": "Approve/Reject Estimate",
"partsCostColon": "Parts Cost:",
"laborCostColon": "Labor Cost:",
"travelTimeCostColon": "Travel Time Cost:",
"otherCostColon": "Other Cost:",
"assetDetail": "Asset Detail",
"assetColon": "Asset:",
"hourMeterColon": "Hour Meter:",
"vinColon": "VIN:",
"typeColon": "Type:",
"odometerColon": "Odometer:",
"workOrderDetail": "Work Order Detail",
"viewApproveEstimate": "View/Approve Estimate",
"workOrderTypeColon": "Work Order Type:",
"customerReportedIssuesColon": "Issues:",
"workOrderTotalCostColon": "Work Order Total Cost:",
"estimateTotalCostColon": "Estimate Total Cost:",
"timeToCompleteColon": "Time To Complete(Hrs):",
"estimateDetail": "Estimate Detail",
"notesFromCustomerColon": "Notes from Customer:",
"approve": "Approve",
"reject": "Reject",
"approveEstimate": "Approve Estimate",
"rejectEstimate": "Reject Estimate",
"confirmApproveEstimate": "Are you sure you want to approve the estimate?",
"confirmRejectEstimate": "Are you sure you want to reject the estimate?",
"estimateStatusColon": "Estimate Status:",
"technicianNotesColon": "Technician Notes:",
"draft": "Draft",
"awaitingResponse": "Awaiting Response",
"closedByDealer": "Closed By Dealer",
"customerRejected": "Customer Rejected",
"customerApproved": "Customer Approved",
"estimateAttachments": "Estimate Attachments",
"photoDocuments": "Photos and Documentation",
"estimateNotPrepared": "Estimate Not Prepared",
"notAvailable": "N/A",
"signatureColon": "Signature:",
"signature": "Signature",
"requireSignature": "Please make a signature first.",
"clear": "Clear",
"approvedByColon": "Approved By:",
"phoneNumberColon": "Phone Number:",
"requireApprovedBy": "Please input the approver.",
"requirePhoneNumber": "Please input the phone number.",
"willWorkOurselves": "Will perform work ourselves",
"willWorkElsewhere": "Will have work done elsewhere",
"cost": "Cost",
"willNotWork": "Will not have work done",
"willWorkLater": "Will have work done at a later date",
"other": "Other",
"preferNotAnswer": "Prefer Not to Answer",
"reasonColon": "Reason:",
"termOfUse": "Terms of Use",
"requireReason": "Please select or input a reason.",
"submit": "Submit",
"survey": "Survey",
"thanksSurvey": "Your survey has been submitted successfully. Thank you for taking the survey.",
"surveyPrompt": "Please Rate the services that you received on work order {wonum} for your {make}/{model}",
"invoiceDetail": "Invoice Detail",
"awaitingPayment": "Awaiting Payment",
"paymentFailure": "Payment Failure",
"customerPaid": "Paid",
"invoiceStatusColon": "Invoice Status:",
"invoiceTotalCostColon": "Invoice Total Cost:",
"taxesColon": "Taxes:",
"invoiceNotPrepared": "Invoice Not Prepared",
"makePayment": "Make Payment",
"typeMessage": "Type your message here",
"typeComment": "Enter Message Here",
"poNumberColon": "PO Number:",
"requirePONumber": "Please Enter a PO Number.",
"void": "Void",
"nameColon": "Name:",
"requireCustomer": "Please select one customer contact at least.",
"openInApp": "Open In App",
"layout": "Layout",
"resetLayout": "Reset Layout",
"resetPivots": "Reset Pivots",
"confirmResetLayout": "Are you sure you want to reset the layout?",
"selectColumns": "Select Columns",
"column": "Column",
"caption": "Caption",
"postNote": "Post Note",
"postedByColon": "Posted by:",
"emailedToColon": "Emailed to:",
"sendMessage": "Send Message",
"editContacts": "Edit Contacts",
"autoUpdateEnabled": "Auto Updates Enabled",
"autoUpdateDisabled": "Auto Updates Disabled",
"statusLinkIncluded": "Status Link Included",
"statusLinkExcluded": "Status Link Excluded"
}

211
language/fr/res.json Normal file
View File

@ -0,0 +1,211 @@
{
"ver": "#{$X-Res-Ver}",
"title": "Fleet Intelligence\u2122 Internal",
"logout": "Se déconnecter",
"workOrder": "Ordres de travail que vous suivez",
"periodCol": "Période:",
"openedDateCol": "Date d'ouverture:",
"closedDateCol": "Date de fermeture:",
"to": "à",
"threeMonths": "Dans les trois mois",
"thisYear": "Cette année",
"lastYear": "L'année dernière",
"all": "Tout",
"allOpened": "Tout ouvert",
"thisMonth": "Ce mois-ci",
"lastMonth": "Le mois dernier",
"thisQuarter": "Ce trimestre",
"lastQuarter": "Dernier quart",
"yearToDate": "Année à ce jour",
"custom": "Personnalisé",
"refresh": "Rafraîchir",
"exportExcel": "Exporter vers Excel",
"report": "Signaler",
"allItem": "( Tout )",
"ok": "d'accord",
"reset": "Réinitialiser",
"yes": "Oui",
"no": "Non",
"unknownError": "Une erreur inconnue s'est produite, veuillez contacter l'administrateur.",
"searchHolder": "Chercher...",
"workOrderNumber": "Numéro de l'ordre de travail",
"workOrderNumberWidth": 215,
"workOrderType": "Type d'ordre de travail",
"workOrderTypeWidth": 200,
"assignedTo": "Assigné à",
"assignedToWidth": 190,
"dealerName": "Le nom du revendeur",
"dealerNameWidth": 270,
"status": "Statut",
"statusWidth": 180,
"createDate": "Date de création",
"createDateWidth": 150,
"completeDate": "Date de fin",
"completeDateWidth": 130,
"completed": "Complété",
"totalCost": "Coût total",
"totalCostWidth": 100,
"vin": "VIN",
"vinWidth": 150,
"make": "Faire",
"makeColon": "Faire: ",
"makeWidth": 150,
"model": "Modèle",
"modelColon": "Modèle: ",
"modelWidth": 150,
"type": "Taper",
"typeWidth": 150,
"customerCode": "Code client",
"customerWidth": 180,
"userProfile": "Profil de l'utilisateur",
"save": "Sauvegarder",
"cancel": "Annuler",
"userName": "Afficher un nom",
"mobilePhone": "Téléphone portable",
"timeZone": "Fuseau horaire",
"language": "Langue",
"companyName": "Nom de la compagnie",
"companyNameWidth": 270,
"advisor": "Conseiller",
"advisorWidth": 120,
"lastCommunicationText": "Dernier texte de communication",
"lastCommunicationTextWidth": 320,
"lastCommunicationBy": "Dernière communication par",
"lastCommunicationByWidth": 215,
"lastCommunicationDate": "Date de la dernière communication",
"lastCommunicationDateWidth": 240,
"displayClosed": "Affichage fermé (90 jours)",
"companyNameColon": "Nom de la compagnie:",
"woNumberColon": "WO#:",
"savedSuccessfully": "Enregistré avec succès.",
"viewWorkOrder": "Ordre de travail - {0}",
"informationonAssetColon": "Informations sur lactif : ",
"workOrderColon": "Ordre de travail: ",
"statusColon": "Statut:",
"billingInformation": "Informations de facturation",
"estimateTotalAmountColon": "Montant total estimatif: ",
"notesColon": "Notes: ",
"issuesIdentifiedinInspection": "Problèmes relevés lors de linspection",
"identifiedIssues": "PROBLÈMES IDENTIFIÉS",
"inspectionDetail": "DÉTAIL DE L'INSPECTION",
"noIdentifieds": "Aucun problème identifié lors de l'inspection",
"severityLevelColon": "Degré de gravité: ",
"low": "faible",
"medium": "dans",
"high": "haute",
"noQuestions": "Pas de problème.",
"workOrderAttachments": "Pièces jointes aux ordres de travail",
"messages": "Communication client",
"internalComments": "Commentaires internes",
"sentTo": "Envoyé à :",
"sent": "Expédié",
"undelivered": "Non livré",
"delivered": "Livré",
"failed": "Manqué",
"review": "La revue",
"pending": "En attente",
"optOut": "Se désengager",
"landline": "Téléphone fixe",
"confirmSendMessage": "Le message sera envoyé à :",
"confirmContinue": "Voulez-vous continuer ?",
"send": "Envoyer",
"viewWorkOrderInspection": "Afficher linspection des ordres de travail - {0}",
"viewFullInspection": "Voir linspection complète",
"summary": "Résumé",
"welcomeTitle": "Merci pour votre entreprise !",
"workOrderNumberColon": "Numéro de commande de travail :",
"openDateColon": "Date d'ouverture :",
"completedDateColon": "Date d'achèvement :",
"approveRejectEstimate": "Approuver/Rejeter l'estimation",
"partsCostColon": "Coût des pièces :",
"laborCostColon": "Coût du travail:",
"travelTimeCostColon": "Coût du temps de trajet :",
"otherCostColon": "Autre coût :",
"assetDetail": "Détail de l'actif",
"assetColon": "Actif:",
"hourMeterColon": "Compteur horaire :",
"vinColon": "VIN:",
"typeColon": "Taper:",
"odometerColon": "Odomètre:",
"workOrderDetail": "Détail de l'ordre de travail",
"viewApproveEstimate": "Voir/approuver l'estimation",
"workOrderTypeColon": "Type d'ordre de travail :",
"customerReportedIssuesColon": "Problèmes:",
"workOrderTotalCostColon": "Coût total de l'ordre de travail :",
"estimateTotalCostColon": "Estimer le coût total :",
"timeToCompleteColon": "Temps pour terminer (heures) :",
"estimateDetail": "Détail de l'estimation",
"notesFromCustomerColon": "Remarques du client :",
"approve": "Approuver",
"reject": "Rejeter",
"approveEstimate": "Approuver l'estimation",
"rejectEstimate": "Rejeter l'estimation",
"confirmApproveEstimate": "Êtes-vous sûr de vouloir approuver le devis ?",
"confirmRejectEstimate": "Êtes-vous sûr de vouloir rejeter le devis ?",
"estimateStatusColon": "État de l'estimation :",
"technicianNotesColon": "Notes du technicien :",
"draft": "Brouillon",
"awaitingResponse": "En attente de réponse",
"closedByDealer": "Fermé par le concessionnaire",
"customerRejected": "Client rejeté",
"customerApproved": "Approuvé par le client",
"estimateAttachments": "Estimer les pièces jointes",
"photoDocuments": "Photos et documentation",
"estimateNotPrepared": "Estimation non préparée",
"notAvailable": "N / A",
"signatureColon": "Signature:",
"signature": "Signature",
"requireSignature": "Veuillez d'abord faire une signature.",
"clear": "Dégager",
"approvedByColon": "Approuvé par:",
"phoneNumberColon": "Numéro de téléphone:",
"requireApprovedBy": "Veuillez saisir l'approbateur.",
"requirePhoneNumber": "Veuillez saisir le numéro de téléphone.",
"willWorkOurselves": "Effectuerons nous-mêmes le travail",
"willWorkElsewhere": "Aura le travail fait ailleurs",
"cost": "Coût",
"willNotWork": "N'aura pas de travail fait",
"willWorkLater": "Les travaux seront effectués à une date ultérieure",
"other": "Autre",
"preferNotAnswer": "Préfère ne pas répondre",
"reasonColon": "Raison:",
"termOfUse": "Conditions d'utilisation",
"requireReason": "Veuillez sélectionner ou saisir une raison.",
"submit": "Soumettre",
"survey": "Sondage",
"thanksSurvey": "Votre enquête a été soumise avec succès. Merci d'avoir répondu à l'enquête.",
"surveyPrompt": "Veuillez évaluer les services que vous avez reçus sur le bon de travail {wonum} pour votre {make}/{model}",
"invoiceDetail": "Détail de la facture",
"awaitingPayment": "En attente de paiement",
"paymentFailure": "Échec de paiement",
"customerPaid": "Payé",
"invoiceStatusColon": "État de la facture:",
"invoiceTotalCostColon": "Coût total de la facture:",
"taxesColon": "Impôts:",
"invoiceNotPrepared": "Facture non préparée",
"makePayment": "Effectuer le paiement",
"typeMessage": "Tapez votre message ici",
"typeComment": "Entrez le message ici",
"poNumberColon": "Numéro de bon de commande:",
"requirePONumber": "Veuillez saisir un numéro de bon de commande.",
"void": "Vide",
"nameColon": "Nom:",
"requireCustomer": "Veuillez sélectionner au moins un contact client.",
"openInApp": "Ouvrir dans l'application",
"layout": "Disposition",
"resetLayout": "Réinitialiser la mise en page",
"resetPivots": "Réinitialiser les pivots",
"confirmResetLayout": "Voulez-vous vraiment réinitialiser la mise en page?",
"selectColumns": "Sélectionnez les colonnes",
"column": "Colonne",
"caption": "Légende",
"postNote": "Note de poste",
"postedByColon": "Posté par:",
"emailedToColon": "Envoyé par e-mail à:",
"sendMessage": "Envoyer le message",
"editContacts": "Modifier les contacts",
"autoUpdateEnabled": "Mises à jour automatiques activées",
"autoUpdateDisabled": "Mises à jour automatiques désactivées",
"statusLinkIncluded": "Lien d'état inclus",
"statusLinkExcluded": "Lien de statut exclu"
}

211
language/zh-cn/res.json Normal file
View File

@ -0,0 +1,211 @@
{
"ver": "#{$X-Res-Ver}",
"title": "Fleet Intelligence\u2122 Internal",
"logout": "注销",
"workOrder": "您关注的工单",
"periodCol": "时期:",
"openedDateCol": "开启日期:",
"closedDateCol": "关闭日期:",
"to": "至",
"threeMonths": "三个月内",
"thisYear": "今年",
"lastYear": "去年",
"all": "全部",
"allOpened": "所有已打开",
"thisMonth": "这个月",
"lastMonth": "上个月",
"thisQuarter": "这个季度",
"lastQuarter": "上个季度",
"yearToDate": "今年迄今为止",
"custom": "自定义",
"refresh": "刷新",
"exportExcel": "导出到 Excel",
"report": "报告",
"allItem": "( 全部 )",
"ok": "确定",
"reset": "重置",
"yes": "是",
"no": "否",
"unknownError": "发生未知错误,请联系管理员。",
"searchHolder": "搜索……",
"workOrderNumber": "工单号",
"workOrderNumberWidth": 110,
"workOrderType": "工单类型",
"workOrderTypeWidth": 190,
"assignedTo": "分配给",
"assignedToWidth": 190,
"dealerName": "设施点",
"dealerNameWidth": 270,
"status": "状态",
"statusWidth": 180,
"createDate": "创建日期",
"createDateWidth": 130,
"completeDate": "完成日期",
"completeDateWidth": 130,
"completed": "已完成",
"totalCost": "总开销",
"totalCostWidth": 100,
"vin": "VIN",
"vinWidth": 150,
"make": "制造商",
"makeColon": "制造商:",
"makeWidth": 150,
"model": "型号",
"modelColon": "型号:",
"modelWidth": 150,
"type": "类型",
"typeWidth": 150,
"customerCode": "客户代码",
"customerWidth": 180,
"userProfile": "用户资料",
"save": "保存",
"cancel": "取消",
"userName": "显示名称",
"mobilePhone": "手机",
"timeZone": "时区",
"language": "语言",
"companyName": "公司名称",
"companyNameWidth": 270,
"advisor": "顾问",
"advisorWidth": 120,
"lastCommunicationText": "最后通讯文本",
"lastCommunicationTextWidth": 320,
"lastCommunicationBy": "最后沟通者",
"lastCommunicationByWidth": 190,
"lastCommunicationDate": "最后通讯日期",
"lastCommunicationDateWidth": 130,
"displayClosed": "显示已关闭90 天)",
"companyNameColon": "公司名称:",
"woNumberColon": "WO#",
"savedSuccessfully": "保存成功。",
"viewWorkOrder": "工单 - {0}",
"informationonAssetColon": "机器信息:",
"workOrderColon": "工单:",
"statusColon": "状态:",
"billingInformation": "计费信息",
"estimateTotalAmountColon": "评估总金额:",
"notesColon": "备注:",
"issuesIdentifiedinInspection": "检查中发现的问题",
"identifiedIssues": "严重问题",
"inspectionDetail": "检查细节",
"noIdentifieds": "检查未发现问题",
"severityLevelColon": "严重性等级:",
"low": "低",
"medium": "中",
"high": "高",
"noQuestions": "无问题",
"workOrderAttachments": "工单附件",
"messages": "客户沟通",
"internalComments": "内部评论",
"sentTo": "已发给:",
"sent": "已发送",
"undelivered": "未送达",
"delivered": "已送达",
"failed": "失败",
"review": "审查",
"pending": "待定",
"optOut": "离开",
"landline": "座机",
"confirmSendMessage": "消息将发送至:",
"confirmContinue": "您要继续吗?",
"send": "发送",
"viewWorkOrderInspection": "工单检查 - {0}",
"viewFullInspection": "查看完整检查",
"summary": "摘要",
"welcomeTitle": "感谢您的业务!",
"workOrderNumberColon": "工单编号:",
"openDateColon": "开放日期:",
"completedDateColon": "完成日期:",
"approveRejectEstimate": "批准/拒绝评估",
"partsCostColon": "零件成本:",
"laborCostColon": "劳动力成本:",
"travelTimeCostColon": "旅行时间成本:",
"otherCostColon": "其他费用:",
"assetDetail": "资产详情",
"assetColon": "资产:",
"hourMeterColon": "计时器:",
"vinColon": "VIN",
"typeColon": "类型:",
"odometerColon": "里程表:",
"workOrderDetail": "工单详情",
"viewApproveEstimate": "查看/批准评估",
"workOrderTypeColon": "工单类型:",
"customerReportedIssuesColon": "问题:",
"workOrderTotalCostColon": "工单总成本:",
"estimateTotalCostColon": "评估总成本:",
"timeToCompleteColon": "完成时间(小时):",
"estimateDetail": "评估详情",
"notesFromCustomerColon": "客户备注:",
"approve": "批准",
"reject": "拒绝",
"approveEstimate": "批准评估",
"rejectEstimate": "拒绝评估",
"confirmApproveEstimate": "您确定要批准估算吗?",
"confirmRejectEstimate": "您确定要拒绝估算吗?",
"estimateStatusColon": "评估状态:",
"technicianNotesColon": "技术员注意事项:",
"draft": "草稿",
"awaitingResponse": "等待答复",
"closedByDealer": "由经销商关闭",
"customerRejected": "顾客拒绝",
"customerApproved": "顾客同意",
"estimateAttachments": "评估附件",
"photoDocuments": "照片和文档",
"estimateNotPrepared": "评估未准备",
"notAvailable": "无",
"signatureColon": "签名:",
"signature": "签名",
"requireSignature": "请先签名。",
"clear": "清除",
"approvedByColon": "批准人:",
"phoneNumberColon": "电话号码:",
"requireApprovedBy": "请输入审批人。",
"requirePhoneNumber": "请输入电话号码。",
"willWorkOurselves": "将自己执行工作",
"willWorkElsewhere": "将在其他地方完成工作",
"cost": "成本",
"willNotWork": "将不会完成工作",
"willWorkLater": "将在以后完成工作",
"other": "其他",
"preferNotAnswer": "不想回答",
"reasonColon": "原因:",
"termOfUse": "使用条款",
"requireReason": "请选择或输入原因。",
"submit": "提交",
"survey": "调查问卷",
"thanksSurvey": "您的调查已成功提交。 感谢您参加调查。",
"surveyPrompt": "请为您的 {make}/{model} 的工单 {wonum} 获得的服务评分",
"invoiceDetail": "发票明细",
"awaitingPayment": "等待支付",
"paymentFailure": "支付失败",
"customerPaid": "已支付",
"invoiceStatusColon": "发票状态:",
"invoiceTotalCostColon": "发票总费用:",
"taxesColon": "税费:",
"invoiceNotPrepared": "未准备发票",
"makePayment": "付款",
"typeMessage": "在这里输入您的信息",
"typeComment": "在这里输入信息",
"poNumberColon": "订单号:",
"requirePONumber": "请输入采购订单号。",
"void": "无效",
"nameColon": "姓名:",
"requireCustomer": "请至少选择一位客户联系人。",
"openInApp": "在应用程序中打开",
"layout": "布局",
"resetLayout": "重置布局",
"resetPivots": "重置选择条件",
"confirmResetLayout": "您确定要重置布局吗?",
"selectColumns": "选择列",
"column": "列",
"caption": "标题",
"postNote": "发表评论",
"postedByColon": "发表者:",
"emailedToColon": "电邮至:",
"sendMessage": "发送信息",
"editContacts": "编辑联系人",
"autoUpdateEnabled": "自动更新已启用",
"autoUpdateDisabled": "自动更新已禁用",
"statusLinkIncluded": "已包含状态链接",
"statusLinkExcluded": "已排除状态链接"
}

View File

@ -5,26 +5,23 @@ import { createBox, appendMedia } from "./lib";
let r = lang;
export default class CustomerRecordComment {
#container;
#option;
#enter;
#message;
_var = {};
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
@ -32,28 +29,28 @@ export default class CustomerRecordComment {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
}
/**
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag === true;
this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : '';
this._var.enter.disabled = flag === true;
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : '';
}
create() {
const readonly = this.#option.readonly;
const readonly = this._var.option.readonly;
const container = createBox(
createElement('div', null,
createElement('div', div => {
@ -65,7 +62,7 @@ export default class CustomerRecordComment {
createElement('button', button => {
button.className = 'roundbtn button-close';
button.style.backgroundColor = 'transparent';
if (this.#option.hasClose !== true) {
if (this._var.option.hasClose !== true) {
button.style.display = 'none';
return;
}
@ -73,8 +70,8 @@ export default class CustomerRecordComment {
fill: '#000'
}));
button.addEventListener('click', () => {
if (typeof this.#option.onClose === 'function') {
this.#option.onClose();
if (typeof this._var.option.onClose === 'function') {
this._var.option.onClose();
}
})
})
@ -83,16 +80,16 @@ export default class CustomerRecordComment {
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.maxLength = this._var.option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
if (readonly === true) {
enter.disabled = true;
}
this.#enter = enter;
this._var.enter = enter;
container.appendChild(
createElement('div', 'message-bar',
enter,
@ -108,8 +105,8 @@ export default class CustomerRecordComment {
// setTooltip(button, r('P_M3_SENDMESSAGE', 'Send Message'));
setTooltip(button, r('P_CU_POSTNOTE', 'Post Note'));
button.addEventListener('click', () => {
if (typeof this.#option.onAddComment === 'function') {
this.#option.onAddComment(this.text);
if (typeof this._var.option.onAddComment === 'function') {
this._var.option.onAddComment(this.text);
}
})
})
@ -118,9 +115,9 @@ export default class CustomerRecordComment {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
load(data) {
@ -129,7 +126,7 @@ export default class CustomerRecordComment {
for (let comment of data) {
const div = createElement('div', 'item-div');
// if (sendto !== '') {
// sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
// sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + `\n${sendto}`;
// }
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
@ -137,8 +134,8 @@ export default class CustomerRecordComment {
}));
const content = createElement('div', 'item-content');
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && comment.MMSParts?.length > 0) {
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Comment)), mmsParts));
if (comment.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comment.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
@ -155,8 +152,8 @@ export default class CustomerRecordComment {
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@ -4,11 +4,10 @@ import { isEmail, nullOrEmpty, r as lang } from "../../utility";
let r = lang;
export class Contact {
#option;
#refs;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@ -18,7 +17,7 @@ export class Contact {
async show(parent = document.body) {
const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3;
const c = this.#option.contact;
const c = this._var.option.contact;
const contactName = createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
@ -54,7 +53,7 @@ export class Contact {
txt.style.height = '100px';
});
const buttons = [];
if (this.#option.company) {
if (this._var.option.company) {
buttons.push({
text: c == null ? r('P_WO_ADDCONTACTRECORD', 'Add Contact Record') : r('P_WO_EDITCONTACTRECORD', 'Edit Contact Record'),
// tabIndex: tabIndex + 7,
@ -64,8 +63,8 @@ export class Contact {
return false;
}
item.SaveToCustomer = 1;
if (typeof this.#option.onSave === 'function') {
return this.#option.onSave.call(this, item, 'customerrecord');
if (typeof this._var.option.onSave === 'function') {
return this._var.option.onSave.call(this, item, 'customerrecord');
}
}
});
@ -81,8 +80,8 @@ export class Contact {
}
//item.Id = -1;
item.SaveToCustomer = 0;
if (typeof this.#option.onSave === 'function') {
return this.#option.onSave.call(this, item, 'workorder');
if (typeof this._var.option.onSave === 'function') {
return this._var.option.onSave.call(this, item, 'workorder');
}
}
},
@ -92,7 +91,7 @@ export class Contact {
}
);
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title: c == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'),
content: createElement('div', wrapper => {
wrapper.className = 'setting-wrapper';
@ -135,7 +134,7 @@ export class Contact {
} else {
preferences.select('0');
}
this.#refs = {
this._var.refs = {
contactName,
preferences,
contactEmail,
@ -149,35 +148,35 @@ export class Contact {
}
prepare() {
const name = this.#refs.contactName.value;
const pref = this.#refs.preferences.selected.value;
const email = this.#refs.contactEmail.value;
const phone = this.#refs.contactMobile.value;
const opt = this.#refs.checkOpt.querySelector('input').checked;
const notes = this.#refs.contactNotes.value;
const title = this.#option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
const name = this._var.refs.contactName.value;
const pref = this._var.refs.preferences.selected.value;
const email = this._var.refs.contactEmail.value;
const phone = this._var.refs.contactMobile.value;
const opt = this._var.refs.checkOpt.querySelector('input').checked;
const notes = this._var.refs.contactNotes.value;
const title = this._var.option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
if (nullOrEmpty(name)) {
showAlert(title, r('P_CR_CONTACTNAMECANNOTBEEMPTY', 'Contact Name cannot be empty.'), 'warn')
.then(() => this.#refs.contactName.focus());
.then(() => this._var.refs.contactName.focus());
return null;
}
if ((pref == 0 || pref == 2) && nullOrEmpty(phone)) {
showAlert(title, r('P_CR_MOBILECANNOTBEEMPTY', 'Mobile cannot be empty.'), 'warn')
.then(() => this.#refs.contactMobile.focus());
.then(() => this._var.refs.contactMobile.focus());
return null;
}
if (pref == 1 && nullOrEmpty(email)) {
showAlert(title, r('P_CU_EMAILCANNOTBEEMPTY', 'Email cannot be empty.'), 'warn')
.then(() => this.#refs.contactEmail.focus());
.then(() => this._var.refs.contactEmail.focus());
return null;
}
if (!nullOrEmpty(email) && !isEmail(email)) {
showAlert(title, r('P_CR_EMAILISNOTAVALIDEMAILADDRESS', 'The email address is invalid.'), 'warn')
.then(() => this.#refs.contactEmail.focus());
.then(() => this._var.refs.contactEmail.focus());
return null;
}
let contact = this.#option.contact;
let contact = this._var.option.contact;
if (contact == null) {
contact = {};
} else if (contact.OptOut !== opt) {
@ -198,11 +197,10 @@ export class Contact {
}
export class CustomerRecordContact {
#option;
#grid;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@ -214,7 +212,7 @@ export class CustomerRecordContact {
const gridContainer = createElement('div', 'selcontact-grid');
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title,
content: createElement('div', 'selcontact-wrapper',
gridContainer
@ -224,8 +222,8 @@ export class CustomerRecordContact {
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
if (typeof this.#option.onOk === 'function') {
return this.#option.onOk.call(this, this.#grid.source.filter(f => f.selected));
if (typeof this._var.option.onOk === 'function') {
return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.selected));
}
}
},
@ -250,16 +248,16 @@ export class CustomerRecordContact {
{ key: 'Notes', caption: r("P_CR_NOTES", "Notes"), width: 120 }
];
grid.init();
grid.source = this.#option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this.#grid = grid;
grid.source = this._var.option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid = grid;
return result;
}
set source(contacts) {
this.#option.contacts = contacts;
const grid = this.#grid;
this._var.option.contacts = contacts;
const grid = this._var.grid;
if (grid != null) {
this.#grid.source = contacts;
this._var.grid.source = contacts;
}
}
}

View File

@ -1,6 +1,6 @@
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup } from "../../ui";
import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../../utility";
import { createBox, appendMedia, fileSupported, insertFile } from "./lib";
import { createBox, appendMedia, fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
import { Contact, CustomerRecordContact } from "./contact";
import Follower from "./follower";
@ -31,31 +31,22 @@ class NoteCol extends GridColumn {
let r = lang;
export default class CustomerCommunication {
#container;
#option;
#contacts;
#followers;
#buttonFollower;
#enter;
#fileControl;
#file;
#message;
#data = {};
#gridContact;
#gridWo;
_var = {
data: {}
};
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get #autoUpdates() { return this.#container.querySelector('.check-auto-update>input') }
get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true }
get _autoUpdates() { return this._var.container.querySelector('.check-auto-update>input') }
get autoUpdatesEnabled() { return this._autoUpdates?.disabled !== true }
set autoUpdatesEnabled(flag) {
const element = this.#autoUpdates;
const element = this._autoUpdates;
if (element == null) {
return;
}
@ -67,9 +58,9 @@ export default class CustomerCommunication {
element.parentElement?.classList?.remove('disabled');
}
}
get autoUpdates() { return this.#autoUpdates?.checked }
get autoUpdates() { return this._autoUpdates?.checked }
set autoUpdates(flag) {
const element = this.#autoUpdates;
const element = this._autoUpdates;
if (element == null) {
return;
}
@ -77,10 +68,10 @@ export default class CustomerCommunication {
element.dispatchEvent(new Event('change'));
}
get #statusLink() { return this.#container.querySelector('.check-status-link>input') }
get statusLinkEnabled() { return this.#statusLink?.disabled !== true }
get _statusLink() { return this._var.container.querySelector('.check-status-link>input') }
get statusLinkEnabled() { return this._statusLink?.disabled !== true }
set statusLinkEnabled(flag) {
const element = this.#statusLink;
const element = this._statusLink;
if (element == null) {
return;
}
@ -92,9 +83,9 @@ export default class CustomerCommunication {
element.parentElement?.classList?.remove('disabled');
}
}
get statusLink() { return this.#statusLink?.checked }
get statusLink() { return this._statusLink?.checked }
set statusLink(flag) {
const element = this.#statusLink;
const element = this._statusLink;
if (element == null) {
return;
}
@ -106,33 +97,33 @@ export default class CustomerCommunication {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.customer-name>.ui-input').disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-edit-contacts').disabled = flag;
this.#container.querySelector('.button-edit-followers').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.customer-name>.ui-input').disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
this._var.container.querySelector('.button-edit-contacts').disabled = flag;
this._var.container.querySelector('.button-edit-followers').disabled = flag;
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
get file() { return this._var.file || this._var.fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
this._var.fileControl?.remove();
const label = this._var.container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
this._var.fileControl = null;
this._var.file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
@ -140,15 +131,15 @@ export default class CustomerCommunication {
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
this._var.fileControl = f;
this._var.file = f.files[0];
const link = this._var.container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
this._var.fileControl = null;
this._var.file = f;
}
if (label != null) {
label.style.display = '';
@ -158,9 +149,9 @@ export default class CustomerCommunication {
}
}
get customerName() { return this.#container.querySelector('.customer-name>.ui-input')?.value }
get customerName() { return this._var.container.querySelector('.customer-name>.ui-input')?.value }
set customerName(name) {
const element = this.#container.querySelector('.customer-name>.ui-input');
const element = this._var.container.querySelector('.customer-name>.ui-input');
if (element == null) {
return;
}
@ -168,7 +159,7 @@ export default class CustomerCommunication {
}
get contacts() {
return [...this.#contacts.children].filter(el => {
return [...this._var.contacts.children].filter(el => {
return el.querySelector('span').dataset.notsend == "false";
}).map(el => {
const span = el.querySelector('span');
@ -176,11 +167,20 @@ export default class CustomerCommunication {
});
}
set contacts(contacts) {
this.#contacts.replaceChildren();
this.setData('contacts', contacts);
this._var.contacts.replaceChildren();
if (contacts?.length > 0) {
var cs = contacts.sort(function (a, b) {
if (a.Name == b.Name) return 0; return a.Name > b.Name ? 1 : -1;
if (a.Name == b.Name) {
return 0;
}
return a.Name > b.Name ? 1 : -1;
});
const messages = this._var.data.messages;
if (this._var.contactsUpdated !== true && messages?.length > 0) {
updateCustomerName(messages, contacts);
this._var.contactsUpdated = true;
}
for (let c of cs) {
//if (c.OptOut || c.OptOut_BC || c.selected === false) {
// continue;
@ -195,19 +195,28 @@ export default class CustomerCommunication {
const to = pref === '1' ? email : mp;
let icon;
let method;
let tipstr;
if (c.OptOut || c.OptOut_BC || c.selected === false) {
icon = 'times';
method = r('P_CU_OPTEDOUT_COLON', 'Opted Out:');
tipstr = r('P_CU_OPTEDOUT_PROMPT', 'User has opted out of messages');
}
else {
switch (pref) {
case '0':
icon = 'comment-lines';
if (c.MobilePhoneStatus !== 0) {
icon = 'times';
if (c.MobilePhoneStatus === 412) {
// landline
tipstr = r('P_CU_LANDLINE', 'Landline');
}
} else {
icon = 'comment-lines';
}
method = r('P_CU_TEXTSTO_COLON', 'Texts to:');
break;
case '2':
icon = 'mobile';
method = r('P_CU_CALLSTO_COLON', 'Calls to:');
icon = 'phone';
tipstr = r('P_CU_NOMESSAGE', 'No Messages Sent');
break;
default:
icon = 'envelope';
@ -225,14 +234,14 @@ export default class CustomerCommunication {
createIcon('fa-light', icon, { 'fill': (c.OptOut || c.OptOut_BC || c.selected === false) ? 'red' : '' }),
span
);
this.#contacts.appendChild(item);
let tip = `${method} ${to}`;
this._var.contacts.appendChild(item);
let tip = tipstr || `${method} ${to}`;
if (span.scrollWidth > span.offsetWidth) {
tip = r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}\n${tip}`;
}
setTooltip(span, tip);
}
this.#message.scrollTop = this.#message.scrollHeight
this._var.message.scrollTop = this._var.message.scrollHeight
}
}
@ -240,11 +249,11 @@ export default class CustomerCommunication {
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
const link = this.#container.querySelector('.check-status-link');
const link = this._var.container.querySelector('.check-status-link');
if (flag === true) {
link.classList.add('disabled');
} else {
@ -252,31 +261,31 @@ export default class CustomerCommunication {
}
link.querySelector('input').disabled = flag;
const display = flag === true ? 'none' : '';
this.#container.querySelector('.button-edit-contacts').style.display = display;
this.#container.querySelector('.button-edit-followers').style.display = display;
// this.#enter.disabled = flag === true;
this.#container.querySelector('.message-bar').style.display = display;
// this.#container.querySelector('.button-send-message').style.display = display;
this._var.container.querySelector('.button-edit-contacts').style.display = display;
this._var.container.querySelector('.button-edit-followers').style.display = display;
// this._var.enter.disabled = flag === true;
this._var.container.querySelector('.message-bar').style.display = display;
// this._var.container.querySelector('.button-send-message').style.display = display;
}
/**
* @param {boolean} flag
*/
set recordReadonly(flag) {
this.#option.recordReadonly = flag;
if (this.#container == null) {
this._var.option.recordReadonly = flag;
if (this._var.container == null) {
return;
}
this.#container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
}
/**
* @param {String} name
*/
set companyName(name) {
this.#option.companyName = name;
const div = this.#container.querySelector('.title-company');
this._var.option.companyName = name;
const div = this._var.container.querySelector('.title-company');
if (nullOrEmpty(name)) {
div.style.display = 'none';
} else {
@ -288,9 +297,9 @@ export default class CustomerCommunication {
* @param {String} code
*/
set companyCode(code) {
const option = this.#option;
const option = this._var.option;
option.companyCode = code;
const div = this.#container.querySelector('.title-company');
const div = this._var.container.querySelector('.title-company');
if (nullOrEmpty(option.companyName)) {
div.style.display = 'none';
} else {
@ -302,18 +311,18 @@ export default class CustomerCommunication {
}
get followers() {
return [...this.#followers.children].map(el => {
return [...this._var.followers.children].map(el => {
const span = el.querySelector('span');
return { 'Email': span.dataset.email, 'MobilePhone': span.dataset.mp, 'Value': span.dataset.name };
});
}
set followers(followers) {
this.#data.followers = followers;
this.#followers.replaceChildren();
this._var.data.followers = followers;
this._var.followers.replaceChildren();
if (followers?.length > 0) {
this.#container.querySelector('.follower-bar').style.display = '';
setTooltip(this.#buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
this.#container.querySelector('.follower-bar>.bar-list').appendChild(this.#buttonFollower);
this._var.container.querySelector('.follower-bar').style.display = '';
setTooltip(this._var.buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
this._var.container.querySelector('.follower-bar>.bar-list').appendChild(this._var.buttonFollower);
for (let f of followers) {
if (f.OptOut) {
continue;
@ -326,7 +335,7 @@ export default class CustomerCommunication {
tips.push(r('P_CU_EMAILSTO_COLON', 'Emails to:') + ` ${email}`);
}
if (f.SendText) {
tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:' + ` ${mpDisplay}`));
tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:') + ` ${mpDisplay}`);
}
let icon;
if (f.SendText && f.SendEmail) {
@ -348,26 +357,26 @@ export default class CustomerCommunication {
createIcon('fa-light', icon),
span
);
this.#followers.appendChild(item);
this._var.followers.appendChild(item);
if (span.scrollWidth > span.offsetWidth) {
tips.splice(0, 0, r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}`);
}
setTooltip(span, tips.join('\n'));
}
} else {
this.#container.querySelector('.follower-bar').style.display = 'none';
setTooltip(this.#buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers'));
this.#container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this.#buttonFollower)
this._var.container.querySelector('.follower-bar').style.display = 'none';
setTooltip(this._var.buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers'));
this._var.container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this._var.buttonFollower)
}
this.#message.scrollTop = this.#message.scrollHeight
this._var.message.scrollTop = this._var.message.scrollHeight
}
setData(key, data) {
this.#data[key] = data;
this._var.data[key] = data;
}
create() {
const option = this.#option;
const option = this._var.option;
const readonly = option.readonly;
// functions
const checkAutoUpdate = createCheckbox({
@ -428,9 +437,9 @@ export default class CustomerCommunication {
]
);
// contacts
this.#contacts = this.#createContacts(container, option);
this._var.contacts = this._createContacts(container, option);
// followers
this.#followers = this.#createFollowers(container, option);
this._var.followers = this._createFollowers(container, option);
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERMESSAGEHERE', 'Enter Message Here');
@ -442,7 +451,7 @@ export default class CustomerCommunication {
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
enter.addEventListener('paste', e => {
if (option.customerNameVisible === true) {
@ -466,7 +475,7 @@ export default class CustomerCommunication {
// this.file = insertFile(container, file, r);
// }
});
this.#enter = enter;
this._var.enter = enter;
container.appendChild(
createElement('div', div => {
div.className = 'message-bar';
@ -498,52 +507,49 @@ export default class CustomerCommunication {
},
enter,
createElement('div', div => div.style.textAlign = 'right',
createElement('div', div => {
div.className = 'customer-name';
if (option.customerNameVisible !== true) {
div.style.display = 'none';
}
},
createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')),
createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
})
),
createElement('div', selector => {
selector.className = 'file-selector';
if (option.customerNameVisible === true) {
selector.style.display = 'none';
}
},
createElement('div', 'customer-left',
createElement('div', div => {
div.className = 'selector-link';
div.addEventListener('click', () => {
this.#fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this.#fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
div.className = 'customer-name';
if (option.customerNameVisible !== true) {
div.style.display = 'none';
}
},
createIcon('fa-regular', 'link')
createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')),
createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
})
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
createElement('div', 'file-selector',
createElement('div', div => {
div.className = 'selector-link';
div.addEventListener('click', () => {
this._var.fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this._var.fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
)
),
createElement('div', 'prompt-count'),
createElement('button', button => {
@ -575,12 +581,12 @@ export default class CustomerCommunication {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
#createContacts(container, option) {
_createContacts(container, option) {
const readonly = option.readonly;
const recordReadonly = option.recordReadonly;
const contacts = createElement('div', 'bar-list-container');
@ -616,6 +622,7 @@ export default class CustomerCommunication {
createElement('div', div => div.innerText = r('P_CU_EDITCONTACTS', 'Edit Contacts')),
createElement('div', div => {
div.className = 'title-company';
div.style.maxWidth = '540px';
if (nullOrEmpty(option.companyName)) {
div.style.display = 'none';
} else {
@ -652,8 +659,8 @@ export default class CustomerCommunication {
});
const result = option.onSelectCRContacts(list);
}
const r = this.#data.contacts;
this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
const r = this._var.data.contacts;
this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -662,7 +669,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -681,7 +688,7 @@ export default class CustomerCommunication {
if (typeof result?.then === 'function') {
return result.then(r => {
r.map(c => {
for (let cc of this.#data.contacts) {
for (let cc of this._var.data.contacts) {
if (c.Id === cc.Id) {
c.selected = true;
break;
@ -692,7 +699,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#data.contacts.filter(c => c.Id >= 0).map(c => {
this._var.data.contacts.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -729,7 +736,7 @@ export default class CustomerCommunication {
// onMasking: option.onMasking,
company: !nullOrEmpty(option.companyName),
onSave: item => {
const exists = this.#gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone);
const exists = this._var.gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone);
if (exists) {
showAlert(r('P_CR_ADDCONTACT', 'Add Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn');
return false;
@ -738,7 +745,7 @@ export default class CustomerCommunication {
const result = option.onSave(item, true);
if (typeof result?.then === 'function') {
return result.then(r => {
this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -747,7 +754,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -807,7 +814,7 @@ export default class CustomerCommunication {
enabled: item => !item.OptOut && !item.OptOut_BC,
onchanged: function () {
if (typeof option.onChanged === 'function') {
option.onChanged([...This.#gridContact.source, ...This.#gridWo.source]);
option.onChanged([...This._var.gridContact.source, ...This._var.gridWo.source]);
}
},
tooltip: item => item.selected ? r('P_CU_OPTEDIN', 'Opted In') : r('P_CU_OPTEDOUT', 'Opted Out')
@ -850,8 +857,8 @@ export default class CustomerCommunication {
company: !nullOrEmpty(option.companyName),
onSave: (item, _op) => {
const exists =
This.#gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) ||
This.#gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone);
This._var.gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) ||
This._var.gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone);
if (exists) {
showAlert(r('P_CR_EDITCONTACT', 'Edit Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn');
return false;
@ -860,7 +867,7 @@ export default class CustomerCommunication {
const result = option.onSave(item);
if (typeof result?.then === 'function') {
return result.then(r => {
This.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
This._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -869,7 +876,7 @@ export default class CustomerCommunication {
}
return c;
});
This.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
This._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -957,7 +964,7 @@ export default class CustomerCommunication {
}
];
grid.init(pop.container.querySelector('.contacts-record'));
const customerRecords = this.#data.contacts.filter(c => c.Id >= 0).map(c => {
const customerRecords = this._var.data.contacts.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -969,11 +976,11 @@ export default class CustomerCommunication {
grid.extraRows = customerRecords.filter(c => !nullOrEmpty(c.Notes)).length;
grid.source = customerRecords;
grid.selectedRowChanged = index => {
if (index >= 0 && this.#gridWo.selectedIndexes?.length > 0) {
this.#gridWo.selectedIndexes = [];
if (index >= 0 && this._var.gridWo.selectedIndexes?.length > 0) {
this._var.gridWo.selectedIndexes = [];
}
};
this.#gridContact = grid;
this._var.gridContact = grid;
// contacts from work order only
const gridWo = new Grid();
@ -1016,7 +1023,7 @@ export default class CustomerCommunication {
}
];
gridWo.init(pop.container.querySelector('.contacts-wo'));
const workOrderOnly = this.#data.contacts.filter(c => c.Id < 0).map(c => {
const workOrderOnly = this._var.data.contacts.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@ -1028,11 +1035,11 @@ export default class CustomerCommunication {
gridWo.extraRows = workOrderOnly.filter(c => !nullOrEmpty(c.Notes)).length;
gridWo.source = workOrderOnly;
gridWo.selectedRowChanged = index => {
if (index >= 0 && this.#gridContact.selectedIndexes?.length > 0) {
this.#gridContact.selectedIndexes = [];
if (index >= 0 && this._var.gridContact.selectedIndexes?.length > 0) {
this._var.gridContact.selectedIndexes = [];
}
};
this.#gridWo = gridWo;
this._var.gridWo = gridWo;
});
});
})
@ -1072,7 +1079,7 @@ export default class CustomerCommunication {
return contacts;
}
#createFollowers(container, option) {
_createFollowers(container, option) {
const readonly = option.readonly;
const recordReadonly = option.recordReadonly;
const followers = createElement('div', 'bar-list-container');
@ -1086,7 +1093,7 @@ export default class CustomerCommunication {
setTooltip(button, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
button.addEventListener('click', () => {
if (typeof option.onInitFollower === 'function') {
option.onInitFollower(this.#data.followers).then(data => {
option.onInitFollower(this._var.data.followers).then(data => {
if (typeof data === 'string') {
showAlert(r('P_CUSTOMERRECORD', 'Customer Record'), data, 'warn');
return;
@ -1108,13 +1115,13 @@ export default class CustomerCommunication {
}
}
});
var title = this.#data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers');
var title = this._var.data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers');
add.show(title, container);
});
}
});
});
this.#buttonFollower = buttonEditFollower;
this._var.buttonFollower = buttonEditFollower;
container.append(
createElement('div', div => {
div.className = 'contact-bar follower-bar';
@ -1141,8 +1148,16 @@ export default class CustomerCommunication {
load(data, contacts, followers) {
const children = [];
if (data?.length > 0) {
contacts ??= this.#data.contacts;
followers ??= this.#data.allfollowers;
contacts ??= this._var.data.contacts;
followers ??= this._var.data.allfollowers;
this.setData('messages', data);
if (this._var.contactsUpdated !== true) {
const contacts = this._var.data.contacts;
if (contacts?.length > 0) {
updateCustomerName(data, contacts);
this._var.contactsUpdated = true;
}
}
for (let comm of data) {
const div = createElement('div', 'item-div');
let name;
@ -1153,32 +1168,7 @@ export default class CustomerCommunication {
name = c?.Name;
}
name ??= comm.IsReply && String(comm.FormatSender) !== '' ? comm.FormatSender : comm.Sender;
let sendto = '';
if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) {
for (let oriph of comm.OriPhoneNumbers) {
let cname;
const email = isEmail(oriph);
if (contacts?.length > 0) {
let c = email ?
contacts.find(c => c.Email === oriph) :
contacts.find(c => c.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
} else if (followers?.length > 0) {
c = email ?
followers.find(f => f.Email === oriph) :
followers.find(f => f.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
}
}
}
sendto += (cname ?? oriph) + '\n';
}
}
if (sendto !== '') {
sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
}
const sendto = getMessageSendTo(comm, contacts, followers, r)
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
div.innerText = name;
@ -1190,13 +1180,13 @@ export default class CustomerCommunication {
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => {
if (/https?:\/\//i.test(comm.Message)) {
span.innerHTML = escapeEmoji(formatUrl(comm.Message));
span.innerHTML = formatUrl(escapeEmoji(comm.Message));
} else {
span.innerText = escapeEmoji(comm.Message);
}
span.appendChild(mmsParts);
}));
if (comm.IsMMS && comm.MMSParts?.length > 0) {
if (comm.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comm.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
@ -1206,39 +1196,16 @@ export default class CustomerCommunication {
div.classList.add('item-other');
} else {
div.classList.add('item-self');
const [status, statusmsg] = this.#getMessageStatus(comm);
const [status, text, color, tips] = getMessageStatus(comm, r, this._var);
if (status !== -100) {
let statustext;
switch (status) {
case 0:
statustext = r('P_CU_PENDING', 'Pending');
content.style.backgroundColor = '#ffc107';
break;
case 1:
statustext = r('P_WO_SENT', 'Sent');
break;
case 9:
statustext = r('P_MA_FAILED', 'Failed');
content.style.backgroundColor = '#ffc107';
break;
case 10:
statustext = r('P_CU_OPTOUT', 'Opt-Out');
content.style.backgroundColor = '#ffc107';
break;
case 412:
statustext = r('P_CU_LANDLINE', 'Landline');
content.style.backgroundColor = '#ffc107';
break;
default:
statustext = r('P_CU_UNDELIVERED', 'Undelivered');
content.style.backgroundColor = '#ffc107';
break;
if (color != null) {
content.style.backgroundColor = color;
}
const divstatus = createElement('div', div => {
div.className = 'item-status';
div.innerText = statustext;
if (status == -10) {
setTooltip(div, statusmsg);
div.innerText = text;
if (tips != null) {
setTooltip(div, tips);
}
});
content.appendChild(divstatus);
@ -1255,43 +1222,8 @@ export default class CustomerCommunication {
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
}
#getMessageStatus(comm) {
let status = -100; // 没有状态,页面上不显示
const ls = [];
let statusmsg = '';
if (!comm.StatusIncorrect && comm.Participator?.length > 0) {
for (let p of comm.Participator) {
if (!isEmail(p.CustomerNumber)) {
if (ls.indexOf(p.Status) < 0) {
ls.push(p.Status);
}
if (statusmsg.length > 0) {
statusmsg += '\n';
}
statusmsg += `${p.CustomerNumber}: `;
const st = ({
0: r('P_CU_UNDELIVERED', 'Undelivered'),
1: r('P_WO_SENT', 'Sent'),
9: r('P_MA_FAILED', 'Failed')
})[p.Status];
if (st != null) {
statusmsg += st;
}
else
statusmsg += r('P_MA_XXXXXX', 'Unknown');
}
}
}
if (ls.length === 1) {
status = ls[0];
} else if (ls.length > 1) {
status = -10; // 多种状态
}
return [status, statusmsg];
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@ -4,11 +4,10 @@ import { nullOrEmpty, r as lang, contains } from "../../utility";
let r = lang;
export default class Follower {
#option;
#grid;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@ -20,7 +19,7 @@ export default class Follower {
const gridContainer = createElement('div', 'follower-grid');
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title,
content: createElement('div', 'follower-wrapper',
createElement('div', div => div.innerText = r('P_CR_WHODOYOUWANTTORECEIVECUSTOMERNOTIFICATIONS', 'Who do you want to receive customer notifications?')),
@ -31,9 +30,9 @@ export default class Follower {
search.addEventListener('input', () => {
const key = search.value;
if (nullOrEmpty(key)) {
this.#grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
} else {
this.#grid.source = this.#option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true))
this._var.grid.source = this._var.option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true))
.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
}
});
@ -45,8 +44,8 @@ export default class Follower {
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
if (typeof this.#option.onOk === 'function') {
return this.#option.onOk.call(this, this.#grid.source.filter(f => f.Email || f.Text));
if (typeof this._var.option.onOk === 'function') {
return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.Email || f.Text));
}
}
},
@ -76,8 +75,8 @@ export default class Follower {
}
];
grid.init();
grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this.#grid = grid;
grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid = grid;
return result;
}
}

View File

@ -1,33 +1,48 @@
import { createElement, setTooltip, createIcon } from "../../ui";
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
import { createBox, appendMedia } from "./lib";
// import { fileSupported, insertFile } from "./lib";
import { fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
let r = lang;
export default class InternalComment {
#container;
#option;
#enter;
#fileControl;
#file;
#message;
_var = {};
// _var.container;
// _var.option;
// _var.enter;
// _var.fileControl;
// _var.file;
// _var.message;
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
/**
* @param {any} contacts
*/
set contacts(contacts) {
this._var.contacts = contacts;
if (this._var.contactsUpdated !== true && contacts?.length > 0) {
const comments = this._var.comments;
if (comments?.length > 0) {
updateCustomerName(this._var.comments, contacts);
this._var.contactsUpdated = true;
}
}
}
@ -35,21 +50,21 @@ export default class InternalComment {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-post-note').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
this._var.container.querySelector('.button-post-note').disabled = flag;
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
get file() { return this._var.file || this._var.fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
this._var.fileControl?.remove();
const label = this._var.container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
this._var.fileControl = null;
this._var.file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
@ -57,15 +72,15 @@ export default class InternalComment {
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
this._var.fileControl = f;
this._var.file = f.files[0];
const link = this._var.container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
this._var.fileControl = null;
this._var.file = f;
}
if (label != null) {
label.style.display = '';
@ -79,16 +94,17 @@ export default class InternalComment {
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag === true;
this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
this._var.enter.disabled = flag === true;
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
}
create() {
const option = this._var.option;
const container = createBox(
createElement('div', null,
createElement('div', div => {
@ -97,96 +113,93 @@ export default class InternalComment {
})
), []
);
const readonly = this.#option.readonly;
const readonly = option.readonly;
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.maxLength = option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
if (readonly === true) {
enter.disabled = true;
}
// enter.addEventListener('paste', e => {
// const file = e.clipboardData.files[0];
// if (file != null) {
// e.preventDefault();
// this.file = insertFile(container, file, r);
// }
// });
this.#enter = enter;
enter.addEventListener('paste', e => {
const file = e.clipboardData.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file, r);
}
});
this._var.enter = enter;
container.appendChild(
createElement('div', div => {
div.className = 'message-bar';
// div.addEventListener('dragover', e => {
// if (option.readonly !== true) {
// const item = e.dataTransfer.items[0];
// if (item?.kind === 'file') {
// e.preventDefault();
// if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) {
// e.dataTransfer.dropEffect = 'none';
// } else {
// e.dataTransfer.dropEffect = 'link';
// }
// }
// }
// });
// div.addEventListener('drop', e => {
// if (option.readonly !== true) {
// const file = e.dataTransfer.files[0];
// if (file != null) {
// e.preventDefault();
// this.file = insertFile(container, file, r);
// }
// }
// });
div.addEventListener('dragover', e => {
if (option.readonly !== true) {
const item = e.dataTransfer.items[0];
if (item?.kind === 'file') {
e.preventDefault();
if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) {
e.dataTransfer.dropEffect = 'none';
} else {
e.dataTransfer.dropEffect = 'link';
}
}
}
});
div.addEventListener('drop', e => {
if (option.readonly !== true) {
const file = e.dataTransfer.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file, r);
}
}
});
},
enter,
createElement('div', div => div.style.textAlign = 'right',
createElement('div', selector => {
selector.className = 'file-selector';
if (this.#option.noMessage === true) {
selector.style.display = 'none';
}
},
createElement('div', div => {
div.className = 'selector-link';
div.style.display = 'none';
// div.addEventListener('click', () => {
// this.#fileControl?.remove();
// const file = createElement('input', input => {
// input.type = 'file';
// input.accept = fileSupported.join(',');
// input.addEventListener('change', () => {
// const file = insertFile(container, input.files?.[0], r);
// if (file != null) {
// this.file = file;
// }
// });
// });
// div.appendChild(this.#fileControl = file);
// file.dispatchEvent(new MouseEvent('click'));
// });
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
createElement('div', 'customer-left',
createElement('div', 'file-selector',
createElement('div', div => {
div.className = 'selector-link';
// div.style.display = 'none';
div.addEventListener('click', () => {
this._var.fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this._var.fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
)
),
createElement('div', 'prompt-count'),
createElement('button', button => {
button.className = 'roundbtn button-send-message';
button.style.backgroundColor = 'rgb(19, 150, 204)';
if (readonly === true || this.#option.noMessage === true) {
if (readonly === true || option.noMessage === true) {
button.style.display = 'none';
}
button.appendChild(createIcon('fa-solid', 'paper-plane'));
@ -196,9 +209,9 @@ export default class InternalComment {
if (nullOrEmpty(val?.trim())) {
return;
}
if (typeof this.#option.onAddMessage === 'function') {
if (typeof option.onAddMessage === 'function') {
this.loading = true;
this.#option.onAddMessage(this.text);
option.onAddMessage(this.text);
}
})
}),
@ -216,9 +229,9 @@ export default class InternalComment {
if (nullOrEmpty(val?.trim())) {
return;
}
if (typeof this.#option.onAddComment === 'function') {
if (typeof option.onAddComment === 'function') {
this.loading = true;
this.#option.onAddComment(this.text, this.file);
option.onAddComment(this.text, this.file);
}
})
})
@ -227,54 +240,77 @@ export default class InternalComment {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
load(data) {
const children = [];
if (data?.length > 0) {
this._var.comments = data;
if (this._var.contactsUpdated !== true) {
const contacts = this._var.contacts;
if (contacts?.length > 0) {
updateCustomerName(data, contacts);
this._var.contactsUpdated = true;
}
}
for (let comment of data) {
const div = createElement('div', 'item-div');
// if (sendto !== '') {
// sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
// }
const sendto = getMessageSendTo(comment, null, null, r)
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
div.innerText = comment.UserName;
div.innerText = comment.Sender;
if (sendto?.length > 0) {
setTooltip(div, sendto);
}
}));
const content = createElement('div', 'item-content');
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && comment.MMSParts?.length > 0) {
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Message)), mmsParts));
if (comment.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comment.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
}
}
if (comment.FollowUp?.length > 0) {
div.classList.add('item-sent');
const sendto = r('P_CU_SENDTO_COLON', 'Send To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
content.appendChild(createElement('div', div => {
// if (comment.FollowUp?.length > 0) {
// div.classList.add('item-sent');
// const sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
// content.appendChild(createElement('div', div => {
// div.className = 'item-status';
// div.innerText = r('P_WO_SENT', 'Sent');
// setTooltip(div, sendto);
// }));
// }
const [status, text, color, tips] = getMessageStatus(comment, r, this._var);
if (status !== -100) {
if (color != null) {
content.style.backgroundColor = color;
}
const divstatus = createElement('div', div => {
div.className = 'item-status';
div.innerText = r('P_WO_SENT', 'Sent');
setTooltip(div, sendto);
}));
div.innerText = text;
if (tips != null) {
setTooltip(div, tips);
}
});
content.appendChild(divstatus);
}
div.append(
content,
createElement('div', div => {
div.className = 'item-time';
div.innerText = comment.SubmitDateStr;
div.innerText = comment.TimeStr;
})
);
children.push(div);
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@ -1,4 +1,5 @@
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile } from "../../ui";
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile, createIcon, Popup, Grid, Dropdown } from "../../ui";
import { global, isEmail } from "../../utility";
export function createBox(title, functions) {
const container = createElement('div', 'comm');
@ -13,22 +14,30 @@ export function createBox(title, functions) {
export function appendMedia(container, mimeType, url) {
switch (mimeType) {
case 'application/pdf':
case '.pdf':
container.appendChild(createFile(url, 'file-pdf'));
break;
case 'application/msword':
case 'application/vnd.ms-word':
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case '.doc':
case '.docx':
container.appendChild(createFile(url, 'file-word'));
break;
case 'application/vnd.ms-excel':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case '.xls':
case '.xlsx':
container.appendChild(createFile(url, 'file-excel'));
break;
case 'application/vnd.ms-powerpoint':
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case '.ppt':
case '.pptx':
container.appendChild(createFile(url, 'file-powerpoint'));
break;
case 'application/smil':
case '.smil':
// TODO: ignore smil files
// container.appendChild(createFile(url, 'smile'));
break;
@ -41,10 +50,18 @@ export function appendMedia(container, mimeType, url) {
case 'audio/opus':
case 'audio/wav':
case 'audio/webm':
case '.aac':
case '.amr':
case '.mp3':
case '.ogg':
case '.opus':
case '.wav':
container.appendChild(createAudio(mimeType, url));
break;
case 'text/plain':
case 'text/x-vcard':
case '.txt':
case '.vcard':
container.appendChild(createFile(url, 'id-card'));
break;
case 'video/3gpp':
@ -54,8 +71,22 @@ export function appendMedia(container, mimeType, url) {
case 'video/x-mpeg':
case 'video/quicktime':
case 'video/webm':
case '.3gp':
case '.3gpp':
case '.mp4':
case '.mpg':
case '.mov':
case '.webm':
container.appendChild(createVideo(url));
break;
case '.jpg':
case '.jpeg':
case '.jfif':
case '.png':
case '.gif':
case '.bmp':
container.appendChild(createPicture(url));
break;
default:
if (/^image\//.test(mimeType)) {
container.appendChild(createPicture(url));
@ -151,4 +182,295 @@ export function insertFile(container, file, r) {
}
return file;
}
};
};
function getStatusText(status, dict) {
switch (status) {
case 0:
case 1:
case 5:
case 6:
return dict[status];
case 9:
case 10:
case 412:
return dict[9];
default:
return dict[9999];
}
}
const SymbolDropdown = Symbol.for('ui-dropdown');
class DropdownColumn {
static create(col, trigger, parent) {
const drop = new Dropdown({ ...col.dropOptions, parent });
drop.onselected = trigger;
return drop.create();
}
static _getDrop(element) {
const dropGlobal = global[SymbolDropdown];
if (dropGlobal == null) {
return null;
}
const dropId = element.dataset.dropId;
const drop = dropGlobal[dropId];
if (drop == null) {
return null;
}
return drop;
}
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
if (data != null) {
val = data.text;
}
super.setValue(element, val);
}
static setValue(element, val, _item, col) {
if (element.tagName !== 'DIV') {
this._setValue(col.source, element, val);
return;
}
const drop = this._getDrop(element);
if (drop == null) {
return;
}
if (drop.source == null || drop.source.length === 0) {
let source = col.source;
if (source != null) {
drop.source = source;
}
}
drop.select(val, true);
}
static getValue(e) {
return e.value;
}
}
export function getMessageStatus(comm, r, _var) {
const messageStatus = {
0: r('P_CU_PENDING', 'Pending'),
1: r('P_WO_SENT', 'Sent'),
5: r('P_CU_DELIVERYCONFIRMED', 'Delivery Confirmed'),
6: r('P_CU_RESENT', 'Resent'),
9: r('P_MA_FAILED', 'Failed'),
9999: r('P_CU_UNKNOWN', 'Unknown')
};
const knownStatus = [0, 1, 5, 6, 9, 10, 412];
const okStatus = [1, 5, 6];
const failedStatus = [9, 10, 412];
let status = -100; // 没有状态,页面上不显示
const ls = [];
const msgs = [];
if (!comm.StatusIncorrect && comm.Participator?.length > 0) {
// if (comm.Id === 433339) {
// comm.Participator[4].Status = 6;
// }
for (let p of comm.Participator) {
if (!isEmail(p.CustomerNumber)) {
if (ls.indexOf(p.Status) < 0) {
ls.push(p.Status);
}
p.statusText = getStatusText(p.Status, messageStatus);
msgs.push(p);
}
}
}
if (ls.length === 1) {
status = ls[0];
} else if (ls.length > 1) {
// status = -10; // 多种状态
status = ls
.filter(s => okStatus.indexOf(s) < 0) // ok status
.sort((a, b) => b - a)[0] ?? 1;
}
const statusText = messageStatus[failedStatus.includes(status) ? 9 : status] ?? messageStatus[9999];
const statusColor = okStatus.includes(status) ? null : '#ffc107';
const statusUpdatable = _var.option.statusUpdatable;
let statusTips;
if (statusUpdatable !== false || ls.length > 1) {
statusTips = createElement('div', tip => {
for (let i = 0; i < msgs.length; i++) {
tip.appendChild(createElement('div', t => {
const p = msgs[i];
if (statusUpdatable !== false && p.StatusChanged) {
t.append(
createElement('span', s => s.innerText = `${p.CustomerNumber}: `),
createElement('span', s => {
s.style.color = '#2140fb';
s.style.cursor = 'pointer';
s.innerText = p.statusText;
s.addEventListener('click', () => {
if (typeof _var.option.onMessageStatusClicked === 'function') {
_var.option.onMessageStatusClicked(p);
}
});
})
)
} else {
t.innerText = `${p.CustomerNumber}: ${p.statusText}`;
}
}));
}
if (statusUpdatable !== false) {
tip.appendChild(createElement('div', b => {
b.className = 'tip-function-button';
// setTooltip(b, r('P_CU_UPDATESTATUS', 'Update Status'));
b.addEventListener('click', async () => {
for (let p of comm.Participator) {
switch (p.Status) {
case 0:
case 1:
case 5:
case 6:
p.statusChanged = String(p.Status);
break;
case 9:
case 10:
case 412:
p.statusChanged = '9';
break;
default:
p.statusChanged = '-1';
break;
}
}
const gridContainer = createElement('div', 'status-grid');
const popup = new Popup({
onMasking: _var.option.onMasking,
title: r('P_CU_UPDATESTATUS', 'Update Status'),
content: createElement('div', wrapper => {
wrapper.className = 'update-status-wrapper';
wrapper.style.width = '500px';
},
gridContainer
),
buttons: [
{
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
const changed = msgs.filter(m => {
switch (m.statusChanged) {
case '-1':
return knownStatus.includes(m.Status);
case '9':
return failedStatus.indexOf(m.Status) < 0;
default:
return String(m.Status) !== m.statusChanged;
}
}).map(m => {
let status = Number(m.statusChanged);
if (isNaN(status) || status < 0) {
status = 9999;
}
return {
Id: m.Id,
Status: status
};
});
if (typeof _var.option.onUpdateMessageStatus === 'function') {
_var.option.onUpdateMessageStatus(changed);
}
}
},
{
text: r('P_WO_CANCEL', 'Cancel'),
key: 'cancel'
}
]
});
await popup.show();
const grid = new Grid(gridContainer);
// grid.headerVisible = false;
grid.allowHtml = true;
grid.columns = [
{
key: 'CustomerNumber',
caption: r('P_JS_NUMBER', 'Number'),
width: 150
},
/*{
key: 'customerName',
caption: r('P_WOS_CUSTOMERNAME', 'Customer Name'),
width: 120
},*/
{
key: 'statusText',
caption: r('P_CU_CURRENTSTATUS', 'Current Status'),
width: 155
},
{
key: 'statusChanged',
caption: r('P_CU_REVISEDSTATUS', 'Revised Status'),
width: 155,
type: DropdownColumn,
source: [
{ value: '-1', text: messageStatus[9999] },
{ value: '0', text: messageStatus[0] },
{ value: '1', text: messageStatus[1] },
{ value: '5', text: messageStatus[5] },
{ value: '6', text: messageStatus[6] },
{ value: '9', text: messageStatus[9] }
]
}
];
grid.init();
grid.source = msgs;
});
}, createIcon('fa-light', 'wave-sine')));
}
});
}
return [status, statusText, statusColor, statusTips];
};
export function getMessageSendTo(comm, contacts, followers, r) {
let sendto = '';
if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) {
for (let oriph of comm.OriPhoneNumbers) {
let cname;
const email = isEmail(oriph);
if (contacts?.length > 0) {
let c = email ?
contacts.find(c => c.Email === oriph) :
contacts.find(c => c.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
} else if (followers?.length > 0) {
c = email ?
followers.find(f => f.Email === oriph) :
followers.find(f => f.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
}
}
}
sendto += (cname ?? oriph) + '\n';
}
}
if (sendto !== '') {
sendto = r('P_CU_SENDTO_COLON', 'Sent to :') + `\n${sendto}`;
}
return sendto;
}
export function updateCustomerName(messages, contacts) {
if (messages?.length > 0 && contacts?.length > 0) {
for (let m of messages) {
if (m.Participator?.length > 0) {
for (let p of m.Participator) {
const contact = contacts.filter(c => c.MobilePhoneDisplayText === p.CustomerNumber)[0];
p.customerName = contact?.Name;
}
}
}
}
}

View File

@ -1,10 +1,24 @@
@import "../../ui/css/functions/func.scss";
.ui-popup-mask .wrapper-edit-method {
width: 100%;
.ui-popup-mask {
.wrapper-edit-method {
width: 100%;
.ui-check-wrapper {
padding: 0 28px;
.ui-check-wrapper {
padding: 0 28px;
}
}
.status-grid,
.contacts-record,
.contacts-wo {
>.ui-grid {
overflow-x: visible;
>.ui-grid-body {
overflow: visible;
}
}
}
}
@ -13,7 +27,7 @@
flex-direction: column;
width: 320px;
background-color: var(--dark-fore-color);
border: 1px solid var(--title-bg-color);
border: 1px solid var(--title-ctrlbg-color);
margin-left: 12px;
& {
@ -69,7 +83,7 @@
flex: 0 0 auto;
padding: 5px 0 5px 10px;
color: var(--title-color);
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
line-height: 24px;
display: flex;
align-items: center;
@ -132,7 +146,7 @@
flex: 0 0 auto;
padding: 4px 0;
display: flex;
border-bottom: 1px solid var(--title-bg-color);
border-bottom: 1px solid var(--title-ctrlbg-color);
position: relative;
>.bar-icon {
@ -253,13 +267,13 @@
.message-bar {
flex: 0 0 auto;
border-bottom: 1px solid var(--title-bg-color);
border-bottom: 1px solid var(--title-ctrlbg-color);
display: flex;
flex-direction: column;
>textarea {
padding: 10px 10px 0;
border: 1px solid var(--title-bg-color);
border: 1px solid var(--title-ctrlbg-color);
border-radius: 5px;
height: 70px;
resize: none;
@ -274,73 +288,75 @@
>div {
padding: 0 10px 10px;
>.customer-name {
>.customer-left {
float: left;
text-align: left;
>span {
font-size: var(--font-smaller-size);
}
>.ui-input {
margin-left: 4px;
width: 150px;
border-top: none;
border-left: none;
border-right: none;
}
}
>.file-selector {
float: left;
display: inline-flex;
align-items: center;
height: 30px;
>.selector-link {
cursor: pointer;
display: flex;
>svg {
width: 16px;
height: 16px;
fill: var(--secondary-link-color);
>.customer-name {
>span {
font-size: var(--font-smaller-size);
}
>input {
display: none;
>.ui-input {
margin-left: 4px;
width: 150px;
border-top: none;
border-left: none;
border-right: none;
}
}
>.selector-name {
max-width: 130px;
padding: 0 20px 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
>.file-selector {
display: inline-flex;
align-items: center;
height: 30px;
+layer {
display: none;
margin-left: -20px;
>.selector-link {
cursor: pointer;
display: flex;
>svg {
width: 16px;
height: 16px;
fill: var(--red-color);
fill: var(--secondary-link-color);
}
&:hover {
>input {
display: none;
}
}
>.selector-name {
max-width: 130px;
padding: 0 20px 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+layer {
display: none;
margin-left: -20px;
cursor: pointer;
>svg {
width: 16px;
height: 16px;
fill: var(--red-color);
}
&:hover {
display: flex;
}
}
&:hover+layer {
display: flex;
}
}
&:hover+layer {
display: flex;
}
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
}
}
}
}
@ -421,6 +437,25 @@
margin-right: -12px;
font-size: .625rem;
float: right;
.ui-tooltip-content .tip-function-button {
text-align: right;
>svg {
width: 20px;
height: 20px;
cursor: pointer;
border: 1px solid;
border-radius: 10px;
padding: 2px;
box-sizing: border-box;
transition: opacity .12s;
&:hover {
opacity: .5;
}
}
}
}
}

6
lib/element.js Normal file
View File

@ -0,0 +1,6 @@
import "./element/style.scss";
import ScheduleItem from "./element/schedule";
export {
ScheduleItem
}

274
lib/element/schedule.js Normal file
View File

@ -0,0 +1,274 @@
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup, Dropdown, validation } from "../ui";
import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../utility";
let r = lang;
function datepicker(element) {
if (typeof $?.fn?.datepicker === 'function') {
$(element).datepicker({
autoHide: true,
format: 'm/dd/yyyy'
});
}
return element;
}
export default class ScheduleItem {
_var = {};
constructor(opt) {
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get checkOccurOnce() { return this._var.container.querySelector('.schedule-id-box-occur-once>input'); }
get checkOccurEvery() { return this._var.container.querySelector('.schedule-id-box-occur-every>input'); }
get inputOccurOnce() { return this._var.container.querySelector('.schedule-id-occur-once'); }
get inputOccurEvery() { return this._var.container.querySelector('.schedule-id-occur-every'); }
get inputOccurStarting() { return this._var.container.querySelector('.schedule-id-occur-starting') }
get inputOccurEnding() { return this._var.container.querySelector('.schedule-id-occur-ending') }
changeDailyFrequency(once) {
this.inputOccurOnce.disabled = !once;
this.inputOccurEvery.disabled = once;
this.inputOccurStarting.disabled = once;
this.inputOccurEnding.disabled = once;
}
getParameters() {
return {
Enabled: this._var.container.querySelector('.schedule-id-enabled>input').checked,
Schedule: {
Frequency: Number(this.dropFrequency.selected.value),
Daily: {
OcurrsOnce: this.checkOccurOnce.checked,
OcurrsOnceAt: this.inputOccurOnce.value,
OcurrsInterval: Number(this.inputOccurEvery.value),
StartingAt: this.inputOccurStarting.value,
EndingAt: this.inputOccurEnding.value
},
Monday: this._var.container.querySelector('.schedule-id-1>input').checked,
Tuesday: this._var.container.querySelector('.schedule-id-2>input').checked,
Wednesday: this._var.container.querySelector('.schedule-id-3>input').checked,
Thursday: this._var.container.querySelector('.schedule-id-4>input').checked,
Friday: this._var.container.querySelector('.schedule-id-5>input').checked,
Saturday: this._var.container.querySelector('.schedule-id-6>input').checked,
Sunday: this._var.container.querySelector('.schedule-id-7>input').checked,
DayOfMonth: Number(this._var.container.querySelector('.schedule-id-dayofmonth').value),
StartDate: this._var.container.querySelector('.schedule-id-duration-start').value,
EndDate: this._var.container.querySelector('.schedule-id-duration-end').value
}
};
}
getDateTime(s) {
if (typeof s === 'string') {
const d = new Date(s);
return isNaN(d.getTime()) ? new Date() : d;
}
return s;
}
getTimeString(s) {
const d = this.getDateTime(s);
return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
}
getDateString(s) {
const d = this.getDateTime(s);
return String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0') + '/' + String(d.getFullYear());
}
setParameters(p) {
this._var.container.querySelector('.schedule-id-enabled>input').checked = p.Enabled;
const schedule = p.Schedule || {};
this.dropFrequency.select(String(schedule.Frequency));
let checker = schedule.Daily.OcurrsOnce ? this.checkOccurOnce : this.checkOccurEvery;
checker.checked = true;
checker.dispatchEvent(new Event('change'));
this.inputOccurOnce.value = this.getTimeString(schedule.Daily.OcurrsOnceAt);
this.inputOccurEvery.value = String(schedule.Daily.OcurrsInterval);
this.inputOccurStarting.value = this.getTimeString(schedule.Daily.StartingAt);
this.inputOccurEnding.value = this.getTimeString(schedule.Daily.EndingAt);
this._var.container.querySelector('.schedule-id-1>input').checked = schedule.Monday;
this._var.container.querySelector('.schedule-id-2>input').checked = schedule.Tuesday;
this._var.container.querySelector('.schedule-id-3>input').checked = schedule.Wednesday;
this._var.container.querySelector('.schedule-id-4>input').checked = schedule.Thursday;
this._var.container.querySelector('.schedule-id-5>input').checked = schedule.Friday;
this._var.container.querySelector('.schedule-id-6>input').checked = schedule.Saturday;
this._var.container.querySelector('.schedule-id-7>input').checked = schedule.Sunday;
this._var.container.querySelector('.schedule-id-dayofmonth').value = String(schedule.DayOfMonth);
const start = this.getDateString(schedule.StartDate);
const end = this.getDateString(schedule.EndDate);
if (typeof $?.fn?.datepicker === 'function') {
$(this._var.container.querySelector('.schedule-id-duration-start')).datepicker('setDate', new Date(start));
$(this._var.container.querySelector('.schedule-id-duration-end')).datepicker('setDate', new Date(end));
} else {
this._var.container.querySelector('.schedule-id-duration-start').value = start;
this._var.container.querySelector('.schedule-id-duration-end').value = end;
}
}
create() {
const option = this._var.option;
const drop = new Dropdown({ selected: '0' });
this.dropFrequency = drop;
drop.source = [
{ value: '0', text: 'Daily' },
{ value: '1', text: 'Weekly' },
{ value: '2', text: 'Monthly' }
];
drop.onselected = item => {
container.querySelector('.schedule-item-weekly').style.display = item.value === '1' ? '' : 'none';
const monthly = item.value === '2';
container.querySelector('.schedule-item-monthly').style.display = monthly ? '' : 'none';
if (!monthly) {
const dayofmonth = this._var.container.querySelector('.schedule-id-dayofmonth');
if (dayofmonth.classList.contains('validation-error')) {
dayofmonth.value = '1';
}
}
};
const container = createElement('div', 'schedule-item-container',
createElement('fieldset', 'schedule-item-frequency',
createElement('legend', legend => legend.innerText = 'Frequency'),
createElement('div', 'schedule-item-line',
createElement('span', span => span.innerText = 'Occurs'),
drop.create()
),
createElement('div', div => {
div.className = 'schedule-item-panel schedule-item-weekly';
div.style.display = 'none';
},
createElement('table', 'schedule-item-table',
createElement('tr', 'schedule-item-tr',
createElement('td', null, createCheckbox({ className: 'schedule-id-1', label: 'Monday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-3', label: 'Wednesday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-5', label: 'Friday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-6', label: 'Saturday' }))
),
createElement('tr', 'schedule-item-tr',
createElement('td', null, createCheckbox({ className: 'schedule-id-2', label: 'Tuesday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-4', label: 'Thursday' })),
createElement('td'),
createElement('td', null, createCheckbox({ className: 'schedule-id-7', label: 'Sunday' }))
)
)
),
createElement('div', div => {
div.className = 'schedule-item-panel schedule-item-monthly';
div.style.display = 'none';
},
createElement('div', 'schedule-item-line',
createElement('span', span => span.innerText = 'On day'),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-dayofmonth', i.maxLength = 2 }),
/^([0]?[1-9]|[12][0-9]|[3][01])$/
),
createElement('span', span => span.innerText = 'of the month')
)
)
),
createElement('fieldset', 'schedule-item-daily-frequency',
createElement('legend', legend => legend.innerText = 'Daily frequency'),
createElement('div', 'schedule-item-line',
createRadiobox({
name: 'schedule-daily-occurs',
checked: true,
className: 'schedule-id-box-occur-once',
label: 'Occurs once at',
onchange: e => this.changeDailyFrequency(e.target.checked)
}),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-once', i.maxLength = 5 }),
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
)
),
createElement('div', 'schedule-item-line schedule-item-line-occur-every',
createRadiobox({
name: 'schedule-daily-occurs',
className: 'schedule-id-box-occur-every',
label: 'Occurs every',
onchange: e => this.changeDailyFrequency(!e.target.checked)
}),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-every', i.maxLength = 5 }),
/^([0][1-9]+|[1-9][0-9]*)$/
),
createElement('span', span => span.innerText = 'minute(s)'),
createElement('div', 'schedule-item-placeholder'),
createElement('div', 'schedule-item-block',
createElement('div', 'scheldule-item-line',
createElement('span', span => span.innerText = 'Starting at'),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-starting', i.maxLength = 5 }),
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
)
),
createElement('div', 'scheldule-item-line',
createElement('span', span => span.innerText = 'Ending at'),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-ending', i.maxLength = 5 }),
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
)
)
)
)
),
createElement('fieldset', 'schedule-item-duration',
createElement('legend', legend => legend.innerText = 'Duration'),
createElement('div', 'schedule-item-line schedule-item-line-duration',
createElement('span', span => span.innerText = 'Start date'),
datepicker(
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-start', i.maxLength = 10 }),
/^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/
)
),
createElement('div', 'schedule-item-placeholder'),
createElement('span', span => span.innerText = 'End date'),
datepicker(
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-end', i.maxLength = 10 }),
/^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/
)
)
),
createElement('div', 'schedule-item-line',
createCheckbox({ className: 'schedule-id-enabled', checked: true, label: 'Enabled' })
)
)
);
this._var.container = container;
if (option.parameter == null) {
option.parameter = {
Enabled: true,
Schedule: {
Frequency: 0,
Daily: {
OcurrsOnce: true,
OcurrsOnceAt: '00:00',
OcurrsInterval: 60,
StartingAt: '00:00',
EndingAt: '23:00'
},
Monday: true,
Tuesday: false,
Wednesday: false,
Thursday: false,
Friday: false,
Saturday: false,
Sunday: false,
DayOfMonth: 1,
StartDate: '',
EndDate: ''
}
};
}
this.setParameters(option.parameter);
return container;
}
}

1
lib/element/style.css Normal file
View File

@ -0,0 +1 @@
.schedule-item-container fieldset{margin-top:10px;border-width:1px;border-radius:4px;border-color:var(--border-color)}.schedule-item-container fieldset legend,.schedule-item-container fieldset span{font-weight:400;font-size:var(--font-size);padding-left:8px;padding-right:6px;color:var(--color)}.schedule-item-container fieldset .ui-input{line-height:20px}.schedule-item-container fieldset .schedule-item-monthly{margin-top:5px}.schedule-item-container fieldset .schedule-item-monthly .ui-input{width:40px}.schedule-item-container fieldset.schedule-item-daily-frequency .ui-input{vertical-align:top;margin-top:5px}.schedule-item-container fieldset .schedule-item-table{width:100%}.schedule-item-container fieldset .schedule-item-line-occur-every{display:flex;align-items:flex-start}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line{display:flex;align-items:center;margin-top:5px}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>span{flex:1 1 auto}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>.ui-input{margin-top:0}.schedule-item-container fieldset .schedule-item-line-occur-every>span{line-height:36px}.schedule-item-container fieldset .schedule-item-line-occur-every .ui-input{width:70px}.schedule-item-container fieldset .schedule-item-line-duration{display:flex;align-items:center;height:36px}.schedule-item-container fieldset .schedule-item-line>.schedule-item-placeholder{flex:1 1 auto}.schedule-item-container .schedule-item-frequency{margin-top:0}

91
lib/element/style.scss Normal file
View File

@ -0,0 +1,91 @@
.schedule-item-container {
fieldset {
margin-top: 10px;
border-width: 1px;
border-radius: 4px;
border-color: var(--border-color);
legend,
span {
font-weight: 400;
font-size: var(--font-size);
padding-left: 8px;
padding-right: 6px;
color: var(--color);
}
.ui-input {
line-height: 20px;
height: 20px;
}
.schedule-item-monthly {
margin-top: 5px;
.ui-input {
width: 40px;
}
}
&.schedule-item-daily-frequency .ui-input {
vertical-align: top;
margin-top: 5px;
}
.schedule-item-table {
width: 100%;
}
.schedule-item-line-occur-every {
display: flex;
align-items: flex-start;
>.schedule-item-block>.scheldule-item-line {
display: flex;
align-items: center;
margin-top: 5px;
>span {
flex: 1 1 auto;
}
>.ui-input {
margin-top: 0;
}
}
>span {
line-height: 36px;
}
.ui-input {
width: 70px;
}
}
.schedule-item-line-duration {
display: flex;
align-items: center;
height: 36px;
}
.schedule-item-line {
>.schedule-item-placeholder {
flex: 1 1 auto;
}
}
}
.schedule-item-frequency {
margin-top: 0;
>.schedule-item-line {
line-height: 24px;
}
}
.ui-drop-wrapper>.ui-drop-header {
height: 24px;
}
}

View File

@ -9,6 +9,7 @@ import { Grid } from "./ui/grid/grid";
import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridTextColumn } from './ui/grid/column';
import { Popup, createPopup, showAlert, showConfirm } from "./ui/popup";
import { createPicture, createAudio, createVideo, createFile } from './ui/media';
import { validation } from './ui/extension';
export {
createElement,
@ -42,5 +43,7 @@ export {
createPicture,
createAudio,
createVideo,
createFile
createFile,
// extension
validation
}

View File

@ -6,6 +6,15 @@
font-family: var(--font-family);
@include outborder();
&.validation-error {
border-color: var(--red-color);
&:focus,
&:hover {
border-color: var(--red-color);
}
}
}
.ui-input {

1
lib/ui/css/grid.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,10 +3,7 @@
.ui-grid {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: visible;
overflow-x: hidden;
overflow: auto;
& {
--cell-hover-bg-color: lightyellow;
@ -52,6 +49,7 @@
}
@include outline();
@include scrollbar();
&,
input[type="text"],
@ -67,321 +65,338 @@
visibility: hidden;
}
>.ui-grid-header {
width: 100%;
min-width: 100%;
margin: 0;
border-bottom: 1px solid var(--header-border-color);
background-color: var(--header-bg-color);
color: var(--header-fore-color);
user-select: none;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
>.ui-grid-wrapper {
position: relative;
tr {
position: relative;
>.ui-grid-table {
position: absolute;
width: 100%;
min-width: 100%;
margin: 0;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
>th {
padding: 0;
margin: 0;
word-wrap: break-word;
white-space: normal;
position: relative;
>thead {
tr {
color: var(--header-fore-color);
position: sticky;
top: 0;
z-index: 2;
>div {
line-height: var(--header-line-height);
min-height: var(--row-height);
display: flex;
align-items: center;
padding: var(--header-padding);
>th {
background-color: var(--header-bg-color);
user-select: none;
padding: 0;
margin: 0;
word-wrap: break-word;
white-space: normal;
position: relative;
&.sticky {
position: sticky;
z-index: 2;
}
>div {
border-bottom: 1px solid var(--header-border-color);
line-height: var(--header-line-height);
min-height: var(--row-height);
display: flex;
align-items: center;
padding: var(--header-padding);
box-sizing: border-box;
// overflow-x: hidden;
>span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
>.arrow {
width: 0;
height: 0;
top: 50%;
margin-top: calc(0px - var(--arrow-size) / 2);
right: calc(var(--arrow-size) / 2);
position: absolute;
&.asc {
border-bottom: var(--arrow-size) solid var(--dark-border-color);
}
&.desc {
border-top: var(--arrow-size) solid var(--dark-border-color);
}
&.asc,
&.desc {
border-left: var(--arrow-size) solid transparent;
border-right: var(--arrow-size) solid transparent;
}
}
>.filter {
width: var(--filter-size);
height: var(--filter-size);
top: 50%;
margin-top: calc(0px - var(--filter-size) / 2);
right: calc(var(--arrow-size) * 2 + 4px);
position: absolute;
display: flex;
>svg {
width: 100%;
height: 100%;
fill: var(--color);
opacity: .2;
transition: opacity .12s ease;
&:hover {
opacity: .8;
}
}
&.hover>svg {
opacity: .8;
}
&.active>svg {
opacity: 1;
}
}
>.spliter {
position: absolute;
height: 100%;
top: 0;
right: calc(0px - var(--split-width) /2);
width: var(--split-width);
cursor: ew-resize;
z-index: 1;
&::after {
content: '';
height: 100%;
width: 1px;
display: block;
margin: 0 auto;
transition: background-color .12s ease;
}
&:hover::after {
background-color: var(--split-border-color);
}
}
>.dragger {
position: absolute;
left: 0;
top: 0;
min-width: var(--dragger-size);
height: 100%;
background-color: var(--dragger-bg-color);
opacity: var(--dragger-opacity);
display: none;
}
>.dragger-cursor {
position: absolute;
top: 0;
height: 100%;
border: 1px solid var(--dragger-cursor-color);
box-sizing: border-box;
margin-left: 0;
opacity: var(--dragger-cursor-opacity);
display: none;
transition: left .12s ease;
&::before {
top: -1px;
border-top: var(--dragger-cursor-size) solid;
}
&::after {
bottom: -1px;
border-bottom: var(--dragger-cursor-size) solid;
}
&::before,
&::after {
content: '';
position: absolute;
left: var(--dragger-cursor-pos);
border-left: var(--dragger-cursor-size) solid transparent;
border-right: var(--dragger-cursor-size) solid transparent;
}
}
&.header-filter>div {
padding: var(--header-filter-padding);
}
}
}
}
>tbody {
color: var(--cell-fore-color);
>.ui-grid-row {
line-height: var(--line-height);
white-space: nowrap;
background-color: var(--row-bg-color);
border-bottom: 1px solid var(--cell-border-color);
box-sizing: border-box;
// overflow-x: hidden;
>span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&:hover {
background-color: var(--row-active-bg-color);
>.arrow {
width: 0;
height: 0;
top: 50%;
margin-top: calc(0px - var(--arrow-size) / 2);
right: calc(var(--arrow-size) / 2);
position: absolute;
&.asc {
border-bottom: var(--arrow-size) solid var(--dark-border-color);
}
&.desc {
border-top: var(--arrow-size) solid var(--dark-border-color);
}
&.asc,
&.desc {
border-left: var(--arrow-size) solid transparent;
border-right: var(--arrow-size) solid transparent;
}
}
>.filter {
width: var(--filter-size);
height: var(--filter-size);
top: 50%;
margin-top: calc(0px - var(--filter-size) / 2);
right: calc(var(--arrow-size) * 2 + 4px);
position: absolute;
display: flex;
>svg {
width: 100%;
height: 100%;
fill: var(--color);
opacity: .2;
transition: opacity .12s ease;
&:hover {
opacity: .8;
>td.sticky {
background-color: var(--row-active-bg-color);
}
}
&.hover>svg {
opacity: .8;
&.selected {
background-color: var(--row-selected-bg-color);
>td.sticky {
background-color: var(--row-selected-bg-color);
}
}
&.active>svg {
>td {
padding: 0;
&.sticky {
position: sticky;
z-index: 1;
background-color: var(--row-bg-color);
}
>span {
padding: var(--spacing-cell);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
>input[type="text"],
>textarea {
border: none;
box-sizing: border-box;
width: 100%;
padding: 0;
@include outline();
&:disabled {
color: var(--text-disabled-color);
}
}
>input[type="text"] {
height: var(--row-height);
text-indent: var(--text-indent);
}
>textarea {
resize: none;
line-height: var(--line-height);
display: block;
padding: var(--spacing-cell);
white-space: nowrap;
@include scrollbar();
}
.ui-check-wrapper {
display: flex;
justify-content: center;
.ui-check-inner {
&,
>svg {
transition: none;
}
}
}
.ui-drop-wrapper {
height: var(--row-height);
width: 100%;
display: flex;
flex-direction: column;
>.ui-drop-header {
border: none;
height: 100%;
>.ui-drop-text {
padding: var(--spacing-cell);
}
}
>.ui-drop-box {
top: calc(var(--row-height) + 2px);
&.slide-up {
top: unset;
bottom: calc(var(--row-height) + 2px);
}
}
}
.col-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
position: relative;
>svg {
width: 16px;
height: 16px;
fill: var(--primary-color);
transition: opacity .12s ease;
}
&:hover>svg {
opacity: .4;
}
&.disabled {
cursor: unset;
>svg {
fill: var(--header-border-color);
opacity: unset;
}
}
}
}
}
.ui-grid-hover-holder {
box-sizing: border-box;
position: absolute;
line-height: var(--line-height);
padding: var(--spacing-cell);
background-color: var(--cell-hover-bg-color);
white-space: pre;
display: flex;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .12s, opacity .12s ease;
&.active {
visibility: visible;
opacity: 1;
}
}
>.spliter {
position: absolute;
height: 100%;
top: 0;
right: calc(0px - var(--split-width) /2);
width: var(--split-width);
cursor: ew-resize;
z-index: 1;
&::after {
content: '';
height: 100%;
width: 1px;
display: block;
margin: 0 auto;
transition: background-color .12s ease;
}
&:hover::after {
background-color: var(--split-border-color);
}
}
>.dragger {
position: absolute;
left: 0;
top: 0;
min-width: var(--dragger-size);
height: 100%;
background-color: var(--dragger-bg-color);
opacity: var(--dragger-opacity);
display: none;
}
>.dragger-cursor {
position: absolute;
top: 0;
height: 100%;
border: 1px solid var(--dragger-cursor-color);
box-sizing: border-box;
margin-left: 0;
opacity: var(--dragger-cursor-opacity);
display: none;
transition: left .12s ease;
&::before {
top: -1px;
border-top: var(--dragger-cursor-size) solid;
}
&::after {
bottom: -1px;
border-bottom: var(--dragger-cursor-size) solid;
}
&::before,
&::after {
content: '';
position: absolute;
left: var(--dragger-cursor-pos);
border-left: var(--dragger-cursor-size) solid transparent;
border-right: var(--dragger-cursor-size) solid transparent;
}
}
&.header-filter>div {
padding: var(--header-filter-padding);
}
}
}
}
>.ui-grid-body {
flex: 1 1 auto;
overflow: auto;
color: var(--cell-fore-color);
@include scrollbar();
.ui-grid-body-content {
position: absolute;
min-width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
>.ui-grid-row {
line-height: var(--line-height);
white-space: nowrap;
background-color: var(--row-bg-color);
border-bottom: 1px solid var(--cell-border-color);
box-sizing: border-box;
&:hover {
background-color: var(--row-active-bg-color);
}
&.selected {
background-color: var(--row-selected-bg-color);
}
>td {
padding: 0;
>span {
padding: var(--spacing-cell);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
>input[type="text"],
>textarea {
border: none;
box-sizing: border-box;
width: 100%;
padding: 0;
@include outline();
&:disabled {
color: var(--text-disabled-color);
}
}
>input[type="text"] {
height: var(--row-height);
text-indent: var(--text-indent);
}
>textarea {
resize: none;
line-height: var(--line-height);
display: block;
padding: var(--spacing-cell);
white-space: nowrap;
@include scrollbar();
}
.ui-check-wrapper {
display: flex;
justify-content: center;
.ui-check-inner {
&,
>svg {
transition: none;
}
}
}
.ui-drop-wrapper {
height: var(--row-height);
width: 100%;
display: flex;
flex-direction: column;
>.ui-drop-header {
border: none;
height: 100%;
>.ui-drop-text {
padding: var(--spacing-cell);
}
}
>.ui-drop-box {
top: calc(var(--row-height) + 2px);
&.slide-up {
top: unset;
bottom: calc(var(--row-height) + 2px);
}
}
}
.col-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
position: relative;
>svg {
width: 16px;
height: 16px;
fill: var(--primary-color);
transition: opacity .12s ease;
}
&:hover>svg {
opacity: .4;
}
&.disabled {
cursor: unset;
>svg {
fill: var(--header-border-color);
opacity: unset;
}
}
}
}
}
}
.ui-grid-hover-holder {
box-sizing: border-box;
position: absolute;
line-height: var(--line-height);
padding: var(--spacing-cell);
background-color: var(--cell-hover-bg-color);
white-space: pre;
display: flex;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .12s, opacity .12s ease;
&.active {
visibility: visible;
opacity: 1;
}
}
}
@ -423,6 +438,7 @@
opacity: 0;
display: flex;
flex-direction: column;
z-index: 3;
&.active {
transform: scaleY(1);

View File

@ -48,7 +48,7 @@ $buttonHeight: 28px;
border-radius: var(--corner-radius) var(--corner-radius) 0 0;
line-height: $headerLineHeight;
user-select: none;
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
color: var(--title-color);
display: flex;
align-items: center;
@ -214,7 +214,7 @@ $buttonHeight: 28px;
text-align: center;
cursor: pointer;
user-select: none;
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
transition: opacity .12s ease;
&:focus,

View File

@ -82,17 +82,18 @@ function filterSource(searchkeys, textkey, key, source) {
}
export class Dropdown {
#options;
_var = {};
// _var.options;
#wrapper;
#container;
#label;
// _var.wrapper;
// _var.container;
// _var.label;
#allChecked;
#source;
#lastSelected;
#selected;
#selectedList;
// _var.allChecked;
// _var.source;
// _var.lastSelected;
// _var.selected;
// _var.selectedList;
sourceFilter;
onselectedlist;
@ -105,18 +106,18 @@ export class Dropdown {
options.valuekey ??= 'value';
options.htmlkey ??= 'html';
options.maxlength ??= 500;
this.#options = options;
this._var.options = options;
}
create() {
const options = this.#options;
const options = this._var.options;
// wrapper
const wrapper = createElement('div', 'ui-drop-wrapper');
const dropId = String(Math.random()).substring(2);
wrapper.dataset.dropId = dropId;
dropdownGlobal[dropId] = this;
this.#wrapper = wrapper;
this._var.wrapper = wrapper;
// header
const header = createElement('div', 'ui-drop-header');
@ -131,8 +132,8 @@ export class Dropdown {
if (up || down) {
const source = this.source;
const count = source.length;
const valuekey = this.#options.valuekey;
let index = source?.indexOf(this.#selected);
const valuekey = this._var.options.valuekey;
let index = source?.indexOf(this._var.selected);
if (isNaN(index) || index < -1) {
index = -1;
} else if (index >= count) {
@ -158,19 +159,19 @@ export class Dropdown {
this.select(target);
}
} else if (e.key === 'Tab') {
this.#dropdown(false);
this._dropdown(false);
}
});
header.addEventListener('click', () => {
if (this.disabled) {
return;
}
const active = this.#expanded;
const label = this.#label;
const active = this._expanded;
const label = this._var.label;
if (active && label.ownerDocument.activeElement === label) {
return;
}
this.#dropdown(!active);
this._dropdown(!active);
if (!active && typeof this.onexpanded === 'function') {
setTimeout(() => this.onexpanded(), 120);
}
@ -187,21 +188,21 @@ export class Dropdown {
label.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this.#container.classList.add('active');
this._filllist(source);
this._var.container.classList.add('active');
});
label.addEventListener('blur', e => this.select(e.target.value));
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
label.addEventListener('mousedown', e => this._expanded && e.stopPropagation());
} else {
isPositive(options.tabIndex) && header.setAttribute('tabindex', options.tabIndex);
label = createElement('label', 'ui-drop-text');
}
this.#label = label;
this._var.label = label;
if (options.multiselect) {
if (Array.isArray(options.selectedlist)) {
this.selectlist(options.selectedlist, true);
} else {
this.#allChecked = true;
this._var.allChecked = true;
label.innerText = r('allItem', '( All )');
}
} else if (options.selected != null) {
@ -214,23 +215,23 @@ export class Dropdown {
return wrapper;
}
get multiselect() { return this.#options.multiselect }
get multiselect() { return this._var.options.multiselect }
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.ui-drop-header.disabled') != null }
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
set disabled(flag) {
if (this.#wrapper == null) {
if (this._var.wrapper == null) {
return;
}
if (flag) {
this.#wrapper.querySelector('.ui-drop-header').classList.add('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.add('disabled');
} else {
this.#wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
}
}
get source() {
let source = this.#source;
let source = this._var.source;
if (source == null || !Array.isArray(source)) {
if (typeof this.sourceFilter === 'function') {
source = this.sourceFilter();
@ -238,7 +239,7 @@ export class Dropdown {
if (!Array.isArray(source)) {
source = [];
}
this.#source = source;
this._var.source = source;
}
return source;
}
@ -247,59 +248,59 @@ export class Dropdown {
if (!Array.isArray(list)) {
return;
}
this.#source = list;
if (this.#expanded) {
setTimeout(() => this.#dropdown(), 120);
this._var.source = list;
if (this._expanded) {
setTimeout(() => this._dropdown(), 120);
}
}
get selected() { return this.#selected }
get selected() { return this._var.selected }
get selectedlist() { return this.#selectedList || [] }
get selectedlist() { return this._var.selectedList || [] }
select(selected, silence) {
if (this.#lastSelected === selected) {
if (this._var.lastSelected === selected) {
return false;
}
this.#lastSelected = selected;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
this._var.lastSelected = selected;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
let item = this.source.find(it => it[valuekey] === selected);
if (this.#options.input) {
if (this._var.options.input) {
if (item == null) {
item = { [valuekey]: selected };
}
this.#label.value = selected;
this._var.label.value = selected;
} else {
const expanded = this.#expanded;
const expanded = this._expanded;
if (expanded) {
this.#container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
this._var.container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
}
if (item == null) {
this.#selected = null;
this.#label.innerText = ' ';
this._var.selected = null;
this._var.label.innerText = ' ';
return false;
}
const html = item[htmlkey];
if (html instanceof HTMLElement) {
this.#label.replaceChildren(html.cloneNode(true));
this._var.label.replaceChildren(html.cloneNode(true));
} else {
let text = item[textkey];
if (nullOrEmpty(text)) {
text = ' ';
}
this.#label.innerText = text;
this._var.label.innerText = text;
}
if (expanded) {
const val = selected.replace(/"/g, '\\"');
const li = this.#container.querySelector(`li[data-value="${val}"]`);
const li = this._var.container.querySelector(`li[data-value="${val}"]`);
if (li != null) {
li.classList.add('selected');
}
}
}
this.#selected = item;
this._var.selected = item;
if (!silence && typeof this.onselected === 'function') {
this.onselected(item);
}
@ -307,9 +308,9 @@ export class Dropdown {
selectlist(selectedlist, silence) {
const source = this.source;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const itemlist = selectedlist.map(v => {
let item = source.find(it => it[valuekey] === v);
if (item == null) {
@ -318,22 +319,22 @@ export class Dropdown {
return item;
});
if (itemlist.length === 0) {
this.#selectedList = null;
this.#label.innerText = none;
this._var.selectedList = null;
this._var.label.innerText = none;
return false;
}
selectItems(this.#label, itemlist, htmlkey, textkey);
this.#selectedList = itemlist;
selectItems(this._var.label, itemlist, htmlkey, textkey);
this._var.selectedList = itemlist;
if (!silence && typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}
}
get #expanded() { return this.#container?.classList?.contains('active') }
get _expanded() { return this._var.container?.classList?.contains('active') }
#dropdown(flag = true) {
const options = this.#options;
let panel = this.#container;
_dropdown(flag = true) {
const options = this._var.options;
let panel = this._var.container;
if (panel == null) {
panel = createElement('div', 'ui-drop-box');
// search box
@ -346,7 +347,7 @@ export class Dropdown {
input.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this._filllist(source);
})
search.append(input, createIcon('fa-light', 'search'));
panel.appendChild(search);
@ -369,8 +370,12 @@ export class Dropdown {
});
}
panel.appendChild(list);
this.#container = panel;
this.#wrapper.appendChild(panel);
this._var.container = panel;
if (options.holder instanceof HTMLElement) {
options.holder.appendChild(panel);
} else {
this._var.wrapper.appendChild(panel);
}
}
if (flag) {
let source = this.source;
@ -380,11 +385,11 @@ export class Dropdown {
source = filterSource(options.searchkeys, options.textkey, search.value, source);
}
}
this.#filllist(source);
this._filllist(source);
// slide direction
if (!options.slidefixed) {
let parent = options.parent ?? document.body;
let p = this.#wrapper;
let p = this._var.wrapper;
let top = p.offsetTop;
while ((p = p.parentElement) != null && p !== parent) {
top -= p.scrollTop;
@ -401,11 +406,11 @@ export class Dropdown {
}
}
#filllist(source) {
const list = this.#container.querySelector('.ui-drop-list');
_filllist(source) {
const list = this._var.container.querySelector('.ui-drop-list');
list.replaceChildren();
const multiselect = this.multiselect;
const allchecked = this.#allChecked;
const allchecked = this._var.allChecked;
if (multiselect) {
list.appendChild(
createElement('li', null,
@ -413,15 +418,15 @@ export class Dropdown {
label: r('allItem', '( All )'),
checked: allchecked,
customAttributes: { 'isall': '1' },
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
})
)
);
}
// TODO: virtual mode
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const selected = this.selected;
const selectedlist = this.selectedlist;
let scrolled;
@ -448,7 +453,7 @@ export class Dropdown {
'class': 'dataitem',
'data-value': val
},
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
});
li.appendChild(box);
} else {
@ -469,43 +474,43 @@ export class Dropdown {
}
}
#triggerselect(checkbox) {
_triggerselect(checkbox) {
let list;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
if (checkbox.getAttribute('isall') === '1') {
const allchecked = this.#allChecked = checkbox.checked;
const boxes = this.#container.querySelectorAll('input.dataitem');
const allchecked = this._var.allChecked = checkbox.checked;
const boxes = this._var.container.querySelectorAll('input.dataitem');
boxes.forEach(box => box.checked = allchecked);
list = [];
} else if (checkbox.checked) {
if (this.#container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this.#allChecked = true;
this.#container.querySelector('input[isall="1"]').checked = true;
if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this._var.allChecked = true;
this._var.container.querySelector('input[isall="1"]').checked = true;
list = [];
} else {
const source = this.source;
list = [...this.#container.querySelectorAll('input.dataitem:checked')]
list = [...this._var.container.querySelectorAll('input.dataitem:checked')]
.map(c => source.find(it => it[valuekey] === c.dataset.value))
.filter(it => it != null);
}
} else {
const val = checkbox.dataset.value;
if (this.#allChecked) {
this.#allChecked = false;
this.#container.querySelector('input[isall="1"]').checked = false;
if (this._var.allChecked) {
this._var.allChecked = false;
this._var.container.querySelector('input[isall="1"]').checked = false;
list = this.source.filter(it => it[valuekey] !== val);
} else {
list = this.selectedlist.filter(it => it[valuekey] !== val);
}
}
if (this.#allChecked) {
this.#label.innerText = r('allItem', '( All )');
if (this._var.allChecked) {
this._var.label.innerText = r('allItem', '( All )');
} else {
selectItems(this.#label, list, htmlkey, textkey);
selectItems(this._var.label, list, htmlkey, textkey);
}
this.#selectedList = list;
this._var.selectedList = list;
if (typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}

1
lib/ui/extension.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export function validation(element: HTMLElement, regex: RegExp) : HTMLElement

12
lib/ui/extension.js Normal file
View File

@ -0,0 +1,12 @@
export function validation(element, regex) {
if (element instanceof HTMLElement && regex instanceof RegExp) {
element.addEventListener('change', e => {
if (regex.test(e.target.value)) {
e.target.classList.remove('validation-error');
} else {
e.target.classList.add('validation-error');
}
})
}
return element;
}

View File

@ -102,12 +102,16 @@ const SymbolDropdown = Symbol.for('ui-dropdown');
export class GridDropdownColumn extends GridColumn {
static createEdit(trigger, col, parent) {
const drop = new Dropdown({ ...col.dropOptions, parent });
const drop = new Dropdown({
...col.dropOptions,
parent,
holder: parent
});
drop.onselected = trigger;
return drop.create();
}
static #getDrop(element) {
static _getDrop(element) {
const dropGlobal = global[SymbolDropdown];
if (dropGlobal == null) {
return null;
@ -120,7 +124,7 @@ export class GridDropdownColumn extends GridColumn {
return drop;
}
static #getSource(item, col) {
static _getSource(item, col) {
let source = col.source;
if (typeof source === 'function') {
source = source(item);
@ -128,7 +132,7 @@ export class GridDropdownColumn extends GridColumn {
return source;
}
static #setValue(source, element, val) {
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
if (data != null) {
val = data.text;
@ -138,20 +142,20 @@ export class GridDropdownColumn extends GridColumn {
static setValue(element, val, item, col) {
if (element.tagName !== 'DIV') {
let source = this.#getSource(item, col);
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => this.#setValue(s, element, val));
source.then(s => this._setValue(s, element, val));
} else {
this.#setValue(source, element, val);
this._setValue(source, element, val);
}
return;
}
const drop = this.#getDrop(element);
const drop = this._getDrop(element);
if (drop == null) {
return;
}
if (drop.source == null || drop.source.length === 0) {
let source = this.#getSource(item, col);
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => {
drop.source = s;
@ -171,7 +175,7 @@ export class GridDropdownColumn extends GridColumn {
static setEnabled(element, enabled) {
super.setEnabled(element , enabled);
const drop = this.#getDrop(element);
const drop = this._getDrop(element);
if (drop == null) {
return;
}

File diff suppressed because it is too large Load Diff

View File

@ -67,7 +67,7 @@ function getTimeLabel(time) {
}
export function createAudio(mime, url) {
if (mime === 'audio/amr' && typeof AMR !== 'undefined') {
if ((mime === 'audio/amr' || mime === '.amr') && typeof AMR !== 'undefined') {
const timestamp = createElement('span', 'ui-media-timestamp');
timestamp.textContent = '00:00 / 00:00';
let context;

View File

@ -39,16 +39,17 @@ function trimPx(px) {
}
export class Popup {
#mask;
#option;
#bounds;
// #cursor;
_var = {};
// _var.mask;
// _var.option;
// _var.bounds;
// _var.cursor;
constructor(opts = {}) {
this.#option = opts;
this._var.option = opts;
}
get container() { return this.#mask.querySelector('.ui-popup-container') }
get container() { return this._var.mask.querySelector('.ui-popup-container') }
get rect() {
const container = this.container;
@ -57,7 +58,7 @@ export class Popup {
}
const style = global.getComputedStyle(container);
const collapsed = container.classList.contains('ui-popup-collapse');
const bounds = this.#bounds;
const bounds = this._var.bounds;
return {
collapsed,
left: trimPx(style.left),
@ -81,7 +82,7 @@ export class Popup {
const collapse = container.querySelector('.ui-popup-header-icons>.icon-expand');
if (r.collapsed === true) {
css.push('width: 160px', 'height: 40px');
this.#bounds = r;
this._var.bounds = r;
container.classList.add('ui-popup-collapse');
if (collapse != null) {
changeIcon(collapse, 'fa-regular', 'expand-alt');
@ -94,7 +95,7 @@ export class Popup {
css.push(`height: ${r.height}px`);
}
container.classList.remove('ui-popup-collapse');
this.#bounds = null;
this._var.bounds = null;
if (collapse != null) {
changeIcon(collapse, 'fa-regular', 'compress-alt');
}
@ -105,7 +106,7 @@ export class Popup {
}
close(animation = true) {
const mask = this.#mask;
const mask = this._var.mask;
if (animation) {
mask.classList.add('ui-popup-active');
mask.style.opacity = 0;
@ -113,17 +114,17 @@ export class Popup {
} else {
mask.remove();
}
if (typeof this.#option.onMasking === 'function') {
this.#option.onMasking.call(this, false);
if (typeof this._var.option.onMasking === 'function') {
this._var.option.onMasking.call(this, false);
}
if (typeof this.#option.resolve === 'function') {
this.#option.resolve();
if (typeof this._var.option.resolve === 'function') {
this._var.option.resolve();
}
}
create() {
const mask = createElement('div', 'ui-popup-mask');
const option = this.#option;
const option = this._var.option;
if (option.mask === false) {
mask.classList.add('ui-popup-transparent');
} else if (typeof option.onMasking === 'function') {
@ -135,7 +136,7 @@ export class Popup {
const container = createElement('div', 'ui-popup-container');
if (option.changeZIndex === true) {
container.addEventListener('mousedown', () => {
const masks = [...this.#mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
const masks = [...this._var.mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
let max = 200;
masks.forEach(m => {
let index;
@ -223,16 +224,16 @@ export class Popup {
});
collapse.addEventListener('click', () => {
if (container.classList.contains('ui-popup-collapse')) {
const bounds = this.#bounds;
const bounds = this._var.bounds;
if (bounds != null) {
container.style.cssText += `width: ${bounds.width}px; height: ${bounds.height}px`;
this.#bounds = null;
this._var.bounds = null;
}
container.classList.remove('ui-popup-collapse');
changeIcon(collapse, 'fa-regular', 'compress-alt');
} else {
const rect = this.rect;
this.#bounds = rect;
this._var.bounds = rect;
container.style.cssText += `width: 160px; height: 40px`;
container.classList.add('ui-popup-collapse');
changeIcon(collapse, 'fa-regular', 'expand-alt');
@ -310,40 +311,40 @@ export class Popup {
container.append(
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.right, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.right, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottom, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottom, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.left, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.left, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.top, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.top, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomRight, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomRight, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomLeft, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomLeft, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topLeft, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.topLeft, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topRight, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.topRight, e));
})
)
}
mask.appendChild(container);
this.#mask = mask;
this._var.mask = mask;
return mask;
}
@ -351,7 +352,7 @@ export class Popup {
if (parent == null) {
return;
}
let mask = this.#mask ?? this.create();
let mask = this._var.mask ?? this.create();
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
const exists = parent.querySelectorAll('.ui-popup-mask');
let zindex = 0;
@ -365,7 +366,7 @@ export class Popup {
mask.style.zIndex = String(zindex + 1);
}
parent.appendChild(mask);
if (this.#option.mask === false) {
if (this._var.option.mask === false) {
// calculator position
const container = this.container;
container.style.left = String((parent.offsetWidth - container.offsetWidth) / 2) + 'px';
@ -380,9 +381,9 @@ export class Popup {
});
}
get loading() { return this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
set loading(flag) {
let loading = this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading');
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
if (loading == null) {
return;
}
@ -395,17 +396,17 @@ export class Popup {
}
}
#resize(mod, e) {
_resize(mod, e) {
if (e.buttons !== 1) {
return;
}
const container = this.container;
const option = this.#option;
const option = this._var.option;
if (typeof option.onResizeStarted === 'function') {
option.onResizeStarted.call(this);
}
const mask = this.#mask;
// this.#cursor = mask.style.cursor;
const mask = this._var.mask;
// this._var.cursor = mask.style.cursor;
// mask.style.cursor = Cursors[mod];
const originalX = e.clientX;
const originalY = e.clientY;
@ -471,7 +472,7 @@ export class Popup {
const up = () => {
parent.removeEventListener('mousemove', move, { passive: false });
parent.removeEventListener('mouseup', up);
// mask.style.cursor = this.#cursor;
// mask.style.cursor = this._var.cursor;
if (resized === true && typeof option.onResizeEnded === 'function') {
option.onResizeEnded.call(this);
}

4
lib/ui/tooltip.d.ts vendored
View File

@ -2,8 +2,8 @@
* tooltip
* @param container tooltip
* @param content html
* @param flag tooltip
* @param parent `container`
* @param [flag=true] tooltip
* @param [parent=null] `container`
*/
export function setTooltip(container: HTMLElement, content: string | HTMLElement, flag?: boolean, parent?: HTMLElement): HTMLElement

View File

@ -91,6 +91,9 @@ export function setTooltip(container, content, flag = false, parent = null) {
let t = c.offsetTop;
let l = c.offsetLeft;
p = c.offsetParent;
if (p == null) {
return;
}
let lastWidth = p.clientWidth;
let lastHeight = p.clientHeight;
while (p != null) {

View File

@ -22,7 +22,7 @@ export function getCookie(name) {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.indexOf(name) === 0) {
if (cookie.startsWith(name)) {
return decodeURIComponent(cookie.substring(name.length));
}
}

View File

@ -10,16 +10,16 @@ export function contains(s, key, ignoreCase) {
key = String(key);
}
if (ignoreCase) {
return s.toLowerCase().indexOf(key.toLowerCase()) >= 0;
return s.toLowerCase().includes(key.toLowerCase());
}
return s.indexOf(key) >= 0;
return s.includes(key);
}
export function endsWith(s, suffix) {
if (nullOrEmpty(s) || nullOrEmpty(suffix)) {
return false;
}
return s.indexOf(suffix) === s.length - suffix.length;
return s.endsWith(suffix); // === s.length - suffix.length;
}
export function padStart(s, num, char) {

28
main.js
View File

@ -1,23 +1,35 @@
import './style.scss'
// import javascriptLogo from './javascript.svg'
import { get } from './lib/utility'
import { createPicture, createAudio, createVideo, createPdf } from './lib/ui/media'
// import { get } from './lib/utility'
// import { createPicture, createAudio, createVideo, createPdf } from './lib/ui/media'
import './lib/element/style.scss'
import ScheduleItem from './lib/element/schedule'
import { createElement } from './lib/functions';
// document.querySelector('#js-logo').src = javascriptLogo
window.consts = {
path: '/',
resver: 20230329
resver: 20231218
}
const schedule = new ScheduleItem();
document.querySelector('#container').replaceChildren(
// createPicture('https://fleet.foresightintelligence.com/doc/mmspart/1740581frZuuFhz5WWCysxs9oGB.jpg'),
createAudio('audio/amr', 'http://vite.tsanie.org/1055003tb0DisaMu1615PeSXKG.amr'),
createPdf('AG-PRO COMPANIES', 'https://fleet.foresightintelligence.com/doc/mmspart/1333321JLrYhkGYqsw6QSVMx3d.pdf'),
// createPicture('https://fleet.foresightintelligence.com/doc/mmspart/138390UGZUMWRmqBsEgPnWuW16.gif'),
// createVideo('https://fleet.foresightintelligence.com/doc/mmspart/17359338sR5qsG7TvS7eaUdP9PL.mp4'),
schedule.create(),
createElement('button', button => {
button.innerText = 'Get';
button.addEventListener('click', () => console.log(schedule.getParameters()));
})
);
// document.querySelector('#container').replaceChildren(
// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/1740581frZuuFhz5WWCysxs9oGB.jpg'),
// createAudio('audio/amr', 'http://vite.tsanie.org/1055003tb0DisaMu1615PeSXKG.amr'),
// createPdf('AG-PRO COMPANIES', 'https://fleet.foresightintelligence.com/doc/mmspart/1333321JLrYhkGYqsw6QSVMx3d.pdf'),
// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/138390UGZUMWRmqBsEgPnWuW16.gif'),
// // createVideo('https://fleet.foresightintelligence.com/doc/mmspart/17359338sR5qsG7TvS7eaUdP9PL.mp4'),
// );
/*
init(null, {
template: '/res.json',

1
style.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -163,7 +163,8 @@ h1 {
}
#container {
flex: 1 1 auto;
// flex: 1 1 auto;
width: 600px;
overflow: auto;
padding: 20px;

View File

@ -56,6 +56,36 @@ const libraries = [
})
]
},
{
clearScreen: false,
css: {
postcss: {
plugins: [postcssPresetEnv()]
}
},
build: {
lib: {
entry: './lib/element.js',
name: 'lib-element',
formats: ['umd'],
fileName: (_format, name) => `${name}.min.js`
},
rollupOptions: {
output: {
assetFileNames: info => info.name === 'style.css' ? 'element.min.css' : info.name
}
},
sourcemap: true,
outDir: '../../../../IronIntel/Contractor2.0/Contractor/Site/js/lib',
emptyOutDir: false
},
plugins: [
viteExternalsPlugin({
'../ui': 'lib-ui',
'../utility': 'lib-utility'
})
]
},
{
clearScreen: false,
build: {