Compare commits

37 Commits

Author SHA256 Message Date
Yuri Becker
76fdc880c7 Implement search 2025-04-24 01:37:09 +02:00
Yuri Becker
45f19d80d0 Use ORM for applicants 2025-04-21 23:45:33 +02:00
Yuri Becker
0ae153617b Fetch applicant from database 2025-04-19 00:55:12 +02:00
Yuri Becker
2832becccf Style EmployeesTable 2025-04-16 12:02:21 +02:00
Yuri Becker
cf5822c950 Somewhat fix errors when adding an applicant 2025-04-16 11:00:06 +02:00
Yuri Becker
f0382a960e Validate applicant and call database procedure 2025-04-15 15:24:31 +02:00
Yuri Becker
a720dfebeb Add fields in ApplicantForm 2025-04-08 11:23:16 +02:00
Yuri Becker
ddf35a55a8 Stub ApplicantForm 2025-04-03 15:33:28 +02:00
Yuri Becker
1e9ba40b6b Add global back button 2025-04-03 14:46:36 +02:00
Yuri Becker
fc9b1c0801 Use original icon names 2025-04-03 13:18:08 +02:00
Yuri Becker
3083406b1b Use common StackView 2025-04-03 13:06:37 +02:00
Yuri Becker
f172468ba6 Extract Employee View into own dir 2025-04-03 10:56:42 +02:00
eda50e036a Merge branch 'main' of https://git.danielsto.de/Linuxero/pyqcrm
merge main
2025-03-25 13:27:56 +01:00
Yuri Becker
7c66dcf890 Add Note to Angebot Erstellen Design 2025-03-25 13:23:19 +01:00
8fec981409 Merge branch 'main' of https://git.danielsto.de/Linuxero/pyqcrm 2025-03-25 13:23:14 +01:00
16ba24b13d offer form not ready 2025-03-25 13:20:54 +01:00
Yuri Becker
309f1f58d7 Fix up Menu 2025-03-25 12:28:30 +01:00
Yuri Becker
445c183e71 Clean up leftover files and commented code 2025-03-25 11:39:45 +01:00
7b8c3630f8 searchbar aligned 2025-03-25 11:15:25 +01:00
d125b56564 Merge branch 'main' of https://git.danielsto.de/Linuxero/pyqcrm 2025-03-25 00:24:28 +01:00
Yuri Becker
7f5675c06d Add Hover Effect to Buttons 2025-03-24 12:06:09 +01:00
Yuri Becker
30493dbdbf Fix Offers View 2025-03-24 11:41:07 +01:00
Yuri Becker
41ecdb658c Fix Objects Table 2025-03-24 11:36:09 +01:00
6e8c2f340c added barbutton, searchbar and qucickfilter positioning, combobox still not finished 2025-03-24 11:31:00 +01:00
76c4be7a7a Merge main 2025-03-24 11:28:57 +01:00
8236bd32e6 positioning searchbar and quickfilter 2025-03-24 11:21:50 +01:00
Yuri Becker
480dd673c4 Fix Mitarbeiter Table 2025-03-24 10:53:46 +01:00
3520c65879 for changing branch 2025-03-24 09:44:55 +01:00
a821956a67 to switch branch 2025-03-21 15:56:05 +01:00
46cba58ebe for branch switch 2025-03-21 15:47:56 +01:00
a7a1cd4913 merge 2025-03-21 15:17:07 +01:00
34bd7409df edited Layout 2025-03-21 14:01:23 +01:00
6df02d9ff2 Merge main 2025-03-21 13:45:53 +01:00
9499eace9b combobox 2025-03-21 13:27:54 +01:00
Yuri Becker
721232a975 Implement Quick Filters 2025-03-21 13:25:03 +01:00
Yuri Becker
83f64f9af8 Use dark/light specification from system 2025-03-20 14:03:40 +01:00
Yuri Becker
dc1ea604f7 Add Icons to Buttons 2025-03-20 13:04:28 +01:00
136 changed files with 2627 additions and 5802 deletions

12
.idea/dataSources.xml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="pyqcrm@bearybot.selfhost.co" uuid="ed28331b-481b-40e7-9295-d9cdae9fd4f2">
<driver-ref>mariadb</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mariadb://bearybot.selfhost.co:8080/pyqcrm</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

4
.idea/discord.xml generated
View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DiscordProjectSettings"> <component name="DiscordProjectSettings">
<option name="show" value="APPLICATION" /> <option name="show" value="PROJECT_FILES" />
<option name="nameOverrideEnabled" value="true" />
<option name="nameOverrideText" value="the qml thing (╯°□°)╯︵ ┻━┻" />
<option name="description" value="" /> <option name="description" value="" />
<option name="applicationTheme" value="default" /> <option name="applicationTheme" value="default" />
<option name="iconsTheme" value="default" /> <option name="iconsTheme" value="default" />

View File

@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

5
.idea/qmlSettings.xml generated
View File

@@ -6,6 +6,11 @@
<entry key=""> <entry key="">
<value> <value>
<PerProfileState> <PerProfileState>
<option name="myExtraQmlPaths">
<list>
<option value="$PROJECT_DIR$/TeroStyle" />
</list>
</option>
<option name="myLSPEnabled" value="true" /> <option name="myLSPEnabled" value="true" />
<option name="myQmlFormatEnabled" value="true" /> <option name="myQmlFormatEnabled" value="true" />
</PerProfileState> </PerProfileState>

6
.idea/sqldialects.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/doc/db_schemer_v1.1-pyqcrm-202503171158_clean.sql" dialect="MariaDB" />
</component>
</project>

View File

@@ -1,180 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "../js/qmldict.js" as JsLib
ColumnLayout
{
id: colPar
anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: parent.width
Label
{
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
id: headline
text: qsTr("Mitarbeiter / Bewerber hinzufügen")
font.pixelSize: 35
}
ButtonGroup
{
buttons: radio.children
onClicked:
{
checkFields()
personalData.requiredField()
}
}
Row
{
Layout.fillWidth: true
id: radio
//Layout.columnSpan: 2
RadioButton
{
checked: true
text: qsTr("Bewerber")
}
RadioButton
{
text: qsTr("Mitarbeiter")
}
}
// ScrollView
// {
// Layout.fillHeight: true
// Layout.fillWidth: true
// implicitWidth: parent.width
// ColumnLayout
// {
// anchors.fill: parent
// //implicitWidth: parent.width
// //width: parent.width
// //height: parent.height
RowLayout
{
Layout.fillWidth: true
//implicitWidth: parent.width
spacing: 50
Frame
{
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
//implicitWidth: parent.width
ApplicantPersonalData
{
id: personalData
width: parent.width
}
}
Frame
{
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
visible: radio.children[1].checked
ColumnLayout
{
Layout.alignment: Qt.AlignTop
implicitWidth: parent.width
ApplicantBankData
{
id: bankAccount
}
ApplicantNationalInsurance
{
id: nationalInsurance
}
ApplicantVarious
{
id: applicantVarious
}
}
}
}
Item
{
Layout.fillHeight: true
}
RowLayout
{
Layout.fillWidth: true
Layout.alignment: Qt.AlignRight
Button
{
text: qsTr("Abbrechen")
onClicked: appLoader.source = "EmployeeTable.qml"
}
Button
{
id: saveBtn
text: qsTr("Speichern")
enabled: false
onClicked:
{
var new_applicant
if (radio.children[0].checked)
{
// Ein Bewerber
new_applicant = JsLib.parseForm(personalData)
employee_model.addEmployee(new_applicant, true)
// appLoader.source = "EmployeeTable.qml"
// console.log(JSON.stringify (new_applicant))
}
else
{
// Ein Mitarbeiter
// console.log(personalData, bankAccount, nationalInsurance, applicantVarious)
new_applicant = JsLib.parseForm(personalData, bankAccount, nationalInsurance, applicantVarious)
employee_model.addEmployee(new_applicant, false)
// var new_contact = JsLib.addApplicant(addContactLayout)
// contact_model.addContact(new_contact)
// console.log(JSON.stringify (new_applicant))
}
}
}
}
Component.onCompleted:
{
employee_model.addedNewEmployee.connect(onAddNewEmployee)
}
// }
// } // ScrollView
function onAddNewEmployee(added)
{
if (added)
console.log('addedsuccesfully')
else
console.log('failedtoadd')
appLoader.source = 'EmployeeTable.qml'
}
function checkFields()
{
if(radio.children[1].checked)
{
if(!personalData.checkPersonalField())
saveBtn.enabled = false
else
saveBtn.enabled = true
}
else if (!personalData.checkPersonalField())
saveBtn.enabled = false
else
saveBtn.enabled = true
}
}

View File

@@ -59,7 +59,7 @@ ColumnLayout
Button Button
{ {
text: qsTr("Abbrechen") text: qsTr("Abbrechen")
onClicked: appLoader.source = "CustomerTable.qml" onClicked: contentStack.pop()
} }
Button Button
{ {
@@ -72,7 +72,7 @@ ColumnLayout
{ {
new_business = JsLib.parseForm(customerView) new_business = JsLib.parseForm(customerView)
business_model.addBusiness(new_business, 0) business_model.addBusiness(new_business, 0)
appLoader.source = "CustomerTable.qml" contentStack.pop()
} }
else else
{ {
@@ -99,7 +99,7 @@ ColumnLayout
{ {
var con_id = arguments[0] var con_id = arguments[0]
business_model.addBusiness(new_business, con_id) business_model.addBusiness(new_business, con_id)
appLoader.source = "CustomerTable.qml" contentStack.pop()
} }
} }

View File

@@ -16,7 +16,7 @@ GridLayout
Label Label
{ {
text: qsTr("Straße") text: qsTr("Straße*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -27,13 +27,11 @@ GridLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
text: qsTr("Nr.") text: qsTr("Nr.*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -44,8 +42,6 @@ GridLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
// New grid row // New grid row
@@ -197,7 +193,7 @@ GridLayout
Label Label
{ {
text: qsTr("Reinigungsmittel wo?") text: qsTr("Reinigungsmittel wo?*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -206,8 +202,6 @@ GridLayout
property string name: "cleaningproducts" property string name: "cleaningproducts"
id: cleaningproducts id: cleaningproducts
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
onTextChanged: checkFields() onTextChanged: checkFields()
} }
Item Item

213
Gui/AddNewOffer.qml Normal file
View File

@@ -0,0 +1,213 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
GridLayout
{
id: newObject
columns: 4
Layout.fillWidth: true
Layout.fillHeight: true
rowSpacing: 9
//New Grid
Label
{
text: qsTr("Objekt:")
Layout.alignment: Qt.AlignRight
font: Typography.h2
}
Item
{
Layout.columnSpan: 3
}
//New grid row
Label
{
text: qsTr("Objekt-Nr.")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "objectno"
id: objectno
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
Button
{
text: qsTr("Objekt hinzufügen")
icon.source: "qrc:/images/PlusCircle.svg"
}
Item
{
}
//// New grid row
Label
{
text: qsTr("Straße")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
TextField
{
property string name: "street"
id: street
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
// onTextChanged: checkFields()
}
Label
{
text: qsTr("Nr.*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
TextField
{
property string name: "houseno"
id: houseno
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
// onTextChanged: checkFields()
}
// New grid row
Label
{
text: qsTr("PLZ")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
ComboBox
{
property string name: "postcode"
id: postcode
Layout.fillWidth: true
editable: true
// onCurrentTextChanged: checkFields()
// onEditTextChanged: checkFields()
onActivated: currentValue
model: address_model
textRole: "display"
popup.height: 300
currentIndex: -1
onCurrentIndexChanged: city.currentIndex = postcode.currentIndex
validator: RegularExpressionValidator
{
regularExpression: /([0-9]{1,5})/
}
}
Label
{
text: qsTr("Ort")
Layout.alignment: Qt.AlignRight
}
ComboBox
{
property string name: "city"
id: city
Layout.fillWidth: true
editable: true
// onEditTextChanged: checkFields()
// onCurrentTextChanged: checkFields()
model: address_model
textRole: "city"
popup.height: 300
currentIndex: -1
}
//New Grid
Label
{
text: qsTr("Kunde:")
Layout.alignment: Qt.AlignRight
font: Typography.h2
}
Item
{
Layout.columnSpan: 3
}
//New grid row
Label
{
text: qsTr("Kunden-Nr.")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "customerno"
id: customerno
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
Button
{
text: qsTr("Kunde hinzufügen")
icon.source: "qrc:/images/PlusCircle.svg"
}
Item
{
}
// New grid row
Label
{
text: qsTr("Kunden-Name")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
TextField
{
id: customerName
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
Item
{
Layout.fillHeight: true
}
// function checkObjectField()
// {
// return street.text.trim() && houseno.text.trim() &&
// (postcode.editText.trim() || postcode.currentText.trim()) &&
// (city.editText.trim() || city.currentText.trim()) &&
// cleaningproducts.text.trim()
// }
}

View File

@@ -61,7 +61,7 @@ ColumnLayout
Button Button
{ {
text: qsTr("Abbrechen") text: qsTr("Abbrechen")
onClicked: appLoader.source = "ObjectTable.qml" onClicked: contentStack.pop()
} }
Button Button
{ {
@@ -105,7 +105,7 @@ ColumnLayout
object_model.viewCriterion("Alle") object_model.viewCriterion("Alle")
} }
appLoader.source = "ObjectTable.qml" contentStack.pop()
} }
} }

112
Gui/AddOffer.qml Normal file
View File

@@ -0,0 +1,112 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Dialogs
import "../js/qmldict.js" as JsLib
ColumnLayout
{
property var new_object: null
//property alias checkAddContact: checkAddContact
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 15
Label
{
text: qsTr("Angebot anlegen")
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pixelSize: 35
}
RowLayout
{
id: addObject
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 45
Frame
{
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
AddNewOffer
{
id: newOffer
width: parent.width
}
}
}
RowLayout
{
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight
Button
{
text: qsTr("Abbrechen")
onClicked: contentStack.pop()
}
Button
{
property var new_object: null
id: saveBtn
text: qsTr("Speichern")
enabled: false
onClicked:
{
// new_object = JsLib.parseForm(newObject)
// new_object['lift'] = new_object['lift'] === 'Ja' ? 1 : 0
// new_object['mezzanin'] = new_object['mezzanin'] === 'Ja' ? 1 : 0
// object_model.addObject(new_object)
}
}
}
Item
{
id: spacer3
Layout.fillHeight: true
}
Component.onCompleted:
{
//object_model.objectAdded.connect(onObjectAdded)
//contact_model.objectContactAdded.connect(onObjectContact)
}
// Connections
// {
// target: object_model
// function onObjectIdReady()
// {
// var obj_id = arguments[0]
// if (checkAddObjectContact.checked && obj_id)
// {
// var new_objecto = addObjectLayout.getForm()
// contact_model.addObjectContact(new_objecto, obj_id)
// object_model.viewCriterion("Alle")
// }
// appLoader.source = "ObjectTable.qml"
// }
// }
// function checkFields()
// {
// if(checkAddObjectContact.checked)
// {
// if(!newObject.checkObjectField() || !addObjectLayout.contactPerson.contacts || !addObjectLayout.contactPerson.contacts.length)
// saveBtn.enabled = false
// else
// saveBtn.enabled = true
// }
// else if (!newObject.checkObjectField())
// saveBtn.enabled = false
// else
// saveBtn.enabled = true
// }
}

View File

@@ -12,8 +12,7 @@ ColumnLayout
Button Button
{ {
text: qsTr("Zurück") text: qsTr("Zurück")
//Layout.columnSpan: 2 onClicked: contentStack.pop()
onClicked: customersStack.pop()
} }
SplitView SplitView

View File

@@ -1,23 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
Item {
anchors.fill: parent
property var availableFilters: ["Name", "Adresse", "PLZ", "Ort"]
id: test
StackView
{
id: customersStack
anchors.fill: parent
initialItem: "CustomersTable.qml"
anchors.topMargin: Dimensions.m
}
}

View File

@@ -16,11 +16,10 @@ GridLayout
Label Label
{ {
id: lblFirmenName id: lblFirmenName
text: qsTr("Firmenname") text: qsTr("Firmenname*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
TextField TextField
{ {
property string name: "business" property string name: "business"
@@ -29,34 +28,26 @@ GridLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
Layout.columnSpan: 3 Layout.columnSpan: 3
} }
Label Label
{ {
text: qsTr("Straße") text: qsTr("Straße*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
TextField TextField
{ {
property string name: "street" property string name: "street"
id: streetid id: streetid
Layout.fillWidth: true Layout.fillWidth: true
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
text: qsTr("Nr.") text: qsTr("Nr.*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -66,8 +57,6 @@ GridLayout
id: housenoid id: housenoid
Layout.fillWidth: true Layout.fillWidth: true
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
validator: RegularExpressionValidator validator: RegularExpressionValidator
{ {
regularExpression: /([0-9a-zA-Z\-]{1,6})/ regularExpression: /([0-9a-zA-Z\-]{1,6})/

View File

@@ -3,175 +3,175 @@ import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.qmlmodels import Qt.labs.qmlmodels
ColumnLayout {
property var availableFilters: ["Name", "Adresse", "PLZ", "Ort"]
Item function viewCriterion(criterion)
{
SearchBar
{ {
id:searchBar business_model.viewCriterion(criterion.text);
anchors.margins: 9
} }
Button
{
id: addBusinessBtn
icon.source: "qrc:/images/addbusiness.svg"
icon.color: "olive"
anchors.right: parent.right
flat: true
onClicked: appLoader.source = "AddCustomer.qml"
}
ColumnLayout
{ anchors.fill: parent
id: tableColumn spacing: Dimensions.l
clip: true
anchors Component.onCompleted: contentStack.pop()
{
top: searchBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
RowLayout RowLayout
{ {
id: sortView Layout.fillWidth: true
RadioButton spacing: Dimensions.l
SearchBar
{ {
id: showAll }
checked: true QuickFilter {
onSelectedChanged: (name) => {
business_model.viewCriterion(name)
}
model: ListModel {
ListElement {
name: "Alle"
text: qsTr("Alle") text: qsTr("Alle")
onClicked: viewCriterion(showAll) selected: true
} }
RadioButton ListElement {
{ name: "Interessent"
id: showInterested
text: qsTr("Interessent") text: qsTr("Interessent")
onClicked: viewCriterion(showInterested) selected: false
} }
RadioButton ListElement {
{ name: "Kunde"
id: showClientele text: qsTr("Kunde")
text: qsTr("Kunden") selected: false
onClicked: viewCriterion(showClientele)
} }
RadioButton ListElement {
{ name: "Lieferant"
id: showProvider
text: qsTr("Lieferant") text: qsTr("Lieferant")
onClicked: viewCriterion(showProvider) selected: false
} }
RadioButton ListElement {
{ name: "Erledigt"
id: showFinished
text: qsTr("Erledigt") text: qsTr("Erledigt")
onClicked: viewCriterion(showFinished) selected: false
} }
} }
}
Button
{
id: addCustomer
Layout.alignment: Qt.AlignRight
icon.source: "qrc:/images/PlusCircle.svg"
text: qsTr("Kunde Hinzufügen")
onClicked: contentStack.push("AddCustomer.qml")
}
}
ColumnLayout
{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.verticalStretchFactor: 1
clip: true
HorizontalHeaderView HorizontalHeaderView
{ {
id: horizontalHeader id: horizontalHeader
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: 40 implicitHeight: 40
movableColumns: true //@disable-check M16 movableColumns: true //@disable-check M16
syncView: customerTable syncView: customerTable
delegate: Rectangle delegate: Rectangle
{ {
color: addBusinessBtn.palette.alternateBase
border.color: addBusinessBtn.palette.base
implicitHeight: 40
Layout.fillWidth: true Layout.fillWidth: true
border.color: addCustomer.palette.base
color: addCustomer.palette.alternateBase
implicitHeight: 40
implicitWidth: 1 implicitWidth: 1
Text Text
{ {
text: model.display color: addCustomer.palette.text
elide: Text.ElideRight elide: Text.ElideRight
width: parent.width
height: parent.height height: parent.height
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: model.display
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: addBusinessBtn.palette.text width: parent.width
} }
} }
} }
TableView {
TableView
{
property real newWidth: 0
id: customerTable id: customerTable
z: 1
property real newWidth: 0
Layout.fillHeight: true Layout.fillHeight: true
//height: tableColumn.height - (sortView.height + horizontalHeader.height)
Layout.fillWidth: true Layout.fillWidth: true
columnSpacing: 1
rowSpacing: 2
model: business_model
alternatingRows: true alternatingRows: true
columnSpacing: 1
model: business_model
resizableColumns: true resizableColumns: true
rowSpacing: 2
selectionBehavior: TableView.SelectRows selectionBehavior: TableView.SelectRows
ScrollBar.vertical: ScrollBar z: 1
{
ScrollBar.vertical: ScrollBar {
policy: customerTable.contentHeight > customerTable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff policy: customerTable.contentHeight > customerTable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
} }
delegate: Rectangle {
selectionModel: ItemSelectionModel
{
id: selModel
model: customerTable.model
}
delegate:Rectangle
{
required property bool selected
required property bool current required property bool current
implicitWidth: customerTable.width / customerTable.columns required property bool selected
color: selected ? addCustomer.palette.highlight //palette.highlight
: (customerTable.alternatingRows && row % 2 !== 0 ? addCustomer.palette.base // palette.base
: addCustomer.palette.alternateBase) //palette.alternateBase)
implicitHeight: 25 implicitHeight: 25
color: selected implicitWidth: customerTable.width / customerTable.columns
? addBusinessBtn.palette.highlight //palette.highlight
: (customerTable.alternatingRows && row % 2 !== 0 Text {
? addBusinessBtn.palette.base // palette.base color: addCustomer.palette.text
: addBusinessBtn.palette.alternateBase) //palette.alternateBase)
Text
{
text: model.display == null? "": model.display // @disable-check M126
elide: Text.ElideRight elide: Text.ElideRight
width: parent.width
height: parent.height height: parent.height
verticalAlignment: Text.AlignVCenter
leftPadding: 9 leftPadding: 9
color: addBusinessBtn.palette.text text: model.display == null ? "" : model.display // @disable-check M126
verticalAlignment: Text.AlignVCenter
width: parent.width
} }
MouseArea MouseArea {
{
property bool hovered: false
id: mouseArea id: mouseArea
property bool hovered: false
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onDoubleClicked:
{
business_model.onRowClicked(row)
customersStack.push("CustomerDetails.qml", {selectedClient: row});
}
onEntered: onDoubleClicked: {
{ business_model.onRowClicked(row);
customerTable.selectionModel.select(customerTable.model.index(row, 0), ItemSelectionModel.SelectCurrent | ItemSelectionModel.Rows) contentStack.push("CustomerDetails.qml", {
selectedClient: row
});
}
onEntered: {
customerTable.selectionModel.select(customerTable.model.index(row, 0), ItemSelectionModel.SelectCurrent | ItemSelectionModel.Rows);
} }
} }
} }
selectionModel: ItemSelectionModel {
id: selModel
model: customerTable.model
}
} }
Item
{
Layout.fillWidth: true
} }
Item {
Layout.fillHeight: true
} }
function viewCriterion(criterion)
{
business_model.viewCriterion(criterion.text)
}
Component.onCompleted: customersStack.pop()
} }

View File

@@ -2,127 +2,106 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
ColumnLayout ColumnLayout {
{ anchors.top: Navigation.bottom
anchors.top: TopBar.bottom
Rectangle Rectangle {
{ Layout.bottomMargin: 3
color: "dimgrey"
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 3 color: "dimgrey"
radius: 45 radius: 45
RowLayout RowLayout {
{
anchors.fill: parent anchors.fill: parent
Rectangle Rectangle {
{
id: contractButton id: contractButton
width: 300
height: 145
color: "darkslategray"
radius: 45
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
border.color: "steelblue" border.color: "steelblue"
border.width: 1 border.width: 1
Text color: "darkslategray"
{ height: 145
radius: 45
width: 300
Text {
anchors.centerIn: parent
color: "lightgoldenrodyellow"
font.bold: true
font.pixelSize: 45
text: qsTr("Aufträge") text: qsTr("Aufträge")
anchors.centerIn: parent
font.pixelSize: 45
font.bold: true
color: "lightgoldenrodyellow"
} }
MouseArea {
MouseArea
{
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
onPressed:
{
contractButton.color = "darkolivegreen"
contractButton.border.width = 3
contractButton.border.color = "skyblue"
console.log("Aufträge...")
}
onReleased: onExited: {
{ contractButton.color = "darkslategray";
contractButton.color = "darkslategray" contractButton.border.color = "steelblue";
contractButton.border.width = 1 contractButton.border.width = 1;
contractButton.border.color = "steelblue"
} }
onHoveredChanged: {
onHoveredChanged: var w = contractButton.border.width === 3 ? 1 : 3;
{ contractButton.border.width = w;
var w = contractButton.border.width === 3? 1: 3
contractButton.border.width = w
} }
onPressed: {
onExited: contractButton.color = "darkolivegreen";
{ contractButton.border.width = 3;
contractButton.border.color = "skyblue";
contractButton.color = "darkslategray" console.log("Aufträge...");
contractButton.border.color = "steelblue" }
contractButton.border.width = 1 onReleased: {
contractButton.color = "darkslategray";
contractButton.border.width = 1;
contractButton.border.color = "steelblue";
} }
} }
} }
Rectangle {
Rectangle
{
id: offerButton id: offerButton
width: 300
height: 145
color: "darkslategray"
radius: 45
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
border.color: "steelblue" border.color: "steelblue"
border.width: 1 border.width: 1
Text color: "darkslategray"
{ height: 145
text: qsTr("Angebote") radius: 45
width: 300
Text {
anchors.centerIn: parent anchors.centerIn: parent
font.pixelSize: 45
font.bold: true
color: "lightgoldenrodyellow" color: "lightgoldenrodyellow"
font.bold: true
font.pixelSize: 45
text: qsTr("Angebote")
} }
MouseArea MouseArea {
{
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
onPressed: onExited: {
{ offerButton.color = "darkslategray";
offerButton.color = "darkolivegreen" offerButton.border.color = "steelblue";
offerButton.border.width = 3 offerButton.border.width = 1;
offerButton.border.color = "skyblue"
console.log("Angebote...")
} }
onReleased: onHoveredChanged: {
{ var w = offerButton.border.width === 3 ? 1 : 3;
offerButton.color = "darkslategray" offerButton.border.width = w;
offerButton.border.width = 1
offerButton.border.color = "steelblue"
} }
onPressed: {
onHoveredChanged: offerButton.color = "darkolivegreen";
{ offerButton.border.width = 3;
var w = offerButton.border.width === 3? 1: 3 offerButton.border.color = "skyblue";
offerButton.border.width = w console.log("Angebote...");
} }
onReleased: {
onExited: offerButton.color = "darkslategray";
{ offerButton.border.width = 1;
offerButton.border.color = "steelblue";
offerButton.color = "darkslategray"
offerButton.border.color = "steelblue"
offerButton.border.width = 1
} }
} }
} }

View File

@@ -1,27 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Item
{
property int selectedEmployee: -1
id: emDet
ColumnLayout
{
Label
{
text: qsTr("Ausgewählter Mitarbeiter " + selectedEmployee)
}
Button
{
text: qsTr("Mitarbeiter zeigen")
onClicked: employeesStack.pop()
}
}
Component.onCompleted:
{
employee_model.onRowClicked(selectedEmployee)
}
}

View File

@@ -1,19 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
Item
{
StackView
{
id: employeesStack
anchors.fill: parent
initialItem: "EmployeesTable.qml"
anchors.topMargin: Dimensions.m
}
}

View File

@@ -0,0 +1,37 @@
import QtQuick
import QtQuick.Layouts
import TeroStyle
ColumnLayout {
anchors.fill: parent
spacing: Dimensions.l
ApplicantForm {
id: applicantForm
Layout.alignment: Qt.AlignTop
Layout.fillHeight: true
Layout.verticalStretchFactor: 1
}
RowLayout {
Layout.alignment: Qt.AlignRight
spacing: Dimensions.l
Button {
icon.source: "qrc:/images/ArrowLeftCircle-Outline.svg"
text: qsTr("Verwerfen")
onClicked: contentStack.pop()
}
Button {
enabled: applicantForm.valid
icon.source: "qrc:/images/CheckCircle.svg"
text: qsTr("Speichern")
onClicked: {
applicantModel.createApplicant(applicantForm.value);
contentStack.pop();
}
}
}
}

View File

@@ -0,0 +1,102 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "../../js/qmldict.js" as JsLib
ColumnLayout {
id: colPar
function checkFields() {
if (!personalData.checkPersonalField())
saveBtn.enabled = false;
else
saveBtn.enabled = true;
}
function onAddNewEmployee(added) {
if (added) {
console.log('addedsuccesfully');
contentStack.pop();
} else {
console.log('failedtoadd');
}
}
Layout.fillHeight: true
Layout.fillWidth: true
anchors.fill: parent
implicitWidth: parent.width
Component.onCompleted: {
employee_model.addedNewEmployee.connect(onAddNewEmployee);
}
Label {
id: headline
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
font.pixelSize: 35
text: qsTr("Mitarbeiter / Bewerber hinzufügen")
}
RowLayout {
Layout.fillWidth: true
spacing: Dimensions.l
Frame {
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.horizontalStretchFactor: 1
EmployeePersonalData {
id: personalData
implicitWidth: parent.width
}
}
Frame {
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.horizontalStretchFactor: 1
ColumnLayout {
Layout.alignment: Qt.AlignTop
implicitWidth: parent.width
EmployeeBankData {
id: bankAccount
}
EmployeeNationalInsurance {
id: nationalInsurance
}
EmployeeVarious {
id: applicantVarious
}
}
}
}
Item {
Layout.fillHeight: true
}
RowLayout {
Layout.alignment: Qt.AlignRight
Layout.fillWidth: true
Button {
text: qsTr("Abbrechen")
onClicked: contentStack.pop()
}
Button {
id: saveBtn
enabled: false
text: qsTr("Speichern")
onClicked: {
const new_applicant = JsLib.parseForm(personalData, bankAccount, nationalInsurance, applicantVarious);
employee_model.addEmployee(new_applicant);
}
}
}
}

View File

@@ -0,0 +1,218 @@
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Layouts
import TeroStyle
ColumnLayout {
readonly property int fieldM: 235
readonly property int fieldS: 110
readonly property bool valid: emailAddress.acceptableInput && firstName.acceptableInput && houseNumber.acceptableInput && lastName.acceptableInput && mobileNumber.acceptableInput && phoneNumber.acceptableInput && salutation.acceptableInput&& street.acceptableInput && title.acceptableInput && zipCode.acceptableInput
readonly property var value: QtObject {
readonly property string emailAddress: emailAddress.text ?? ""
readonly property string firstName: firstName.text ?? ""
readonly property string houseNumber: houseNumber.text ?? ""
readonly property string lastName: lastName.text ?? ""
readonly property string mobileNumber: mobileNumber.text ?? ""
readonly property string phoneNumber: phoneNumber.text ?? ""
readonly property string salutation: salutation.text ?? ""
readonly property string street: street.text ?? ""
readonly property string title: title.currentText
readonly property int zipCode: zipCode.currentIndex
}
function setValue(value) {
title.currentIndex = value.title ?? 0;
firstName.text = value.firstName ?? "";
lastName.text = value.lastName ?? "";
street.text = value.street ?? "";
houseNumber.text = value.houseNumber ?? "";
zipCode.currentIndex = value.zipCode ?? -1;
phoneNumber.text = value.phoneNumber ?? "";
mobileNumber.text = value.mobileNumber ?? "";
emailAddress.text = value.emailAddress ?? "";
salutation.text = value.salutation ?? 0;
}
spacing: Dimensions.l
IconLabel {
color: Colors.foreground
font: Typography.h2
icon.color: Colors.foreground
icon.height: Typography.h2.pixelSize
icon.source: "qrc:/images/UserCircle"
icon.width: Typography.h2.pixelSize
spacing: Dimensions.m
text: qsTr("Stammdaten")
}
RowLayout {
spacing: Dimensions.m
Field {
label: qsTr("Anrede")
ComboBox {
id: title
implicitWidth: fieldM
model: [qsTr("Keine Angabe"), qsTr("Herr"), qsTr("Frau")]
onCurrentTextChanged: {
switch (title.currentIndex) {
case 1:
salutation.text = "Sehr geehrter Herr ";
break;
case 2:
salutation.text = "Sehr geehrte Frau ";
break;
default:
salutation.text = "Guten Tag ";
}
}
}
}
Field {
label: qsTr("Vorname")
mandatory: true
TextField {
id: firstName
implicitWidth: fieldM
placeholderText: qsTr("Max")
validator: NotEmptyValidator {
}
}
}
Field {
label: qsTr("Nachname")
mandatory: true
TextField {
id: lastName
implicitWidth: fieldM
placeholderText: qsTr("Mustermann")
validator: NotEmptyValidator {
}
}
}
}
RowLayout {
spacing: Dimensions.m
Field {
label: qsTr("Straße")
TextField {
id: street
implicitWidth: fieldM
placeholderText: qsTr("Musterstraße")
}
}
Field {
label: qsTr("Hausnummer")
TextField {
id: houseNumber
implicitWidth: fieldS
placeholderText: qsTr("1a")
}
}
Field {
label: qsTr("PLZ")
ComboBox {
id: zipCode
currentIndex: -1
editable: true
implicitWidth: fieldS
model: address_model
textRole: "display"
onActivated: currentValue
onCurrentIndexChanged: city.currentIndex = zipCode.currentIndex
}
}
Field {
label: qsTr("Ort")
ComboBox {
id: city
currentIndex: -1
editable: true
implicitWidth: fieldM
model: address_model
textRole: "city"
}
}
}
IconLabel {
color: Colors.foreground
font: Typography.h2
icon.color: Colors.foreground
icon.height: Typography.h2.pixelSize
icon.source: "qrc:/images/Phone"
icon.width: Typography.h2.pixelSize
spacing: Dimensions.m
text: qsTr("Kontakt")
}
RowLayout {
spacing: Dimensions.m
Field {
label: qsTr("Telefonnummer")
TextField {
id: phoneNumber
implicitWidth: fieldM
placeholderText: "+49 1234 567890"
validator: OptionalPhoneNumberValidator {
}
}
}
Field {
label: qsTr("Mobil")
TextField {
id: mobileNumber
implicitWidth: fieldM
placeholderText: "+49 123 4567891011"
validator: OptionalPhoneNumberValidator {
}
}
}
Field {
label: qsTr("E-Mail Adresse")
TextField {
id: emailAddress
implicitWidth: fieldM
placeholderText: "tero@example.org"
validator: OptionalEmailAddressValidator {
}
}
}
Field {
label: qsTr("Briefanrede")
TextField {
id: salutation
implicitWidth: fieldM
}
}
}
}

View File

@@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import TeroStyle
ColumnLayout {
property int row: -1
anchors.fill: parent
spacing: Dimensions.l
onRowChanged: {
if (row !== -1) {
applicantForm.setValue(applicantModel.applicant(row))
}
}
ApplicantForm {
id: applicantForm
Layout.alignment: Qt.AlignTop
Layout.fillHeight: true
Layout.verticalStretchFactor: 1
}
RowLayout {
spacing: Dimensions.l
Button {
text: qsTr("Als Mitarbeiter:in Einstellen")
icon.source: "qrc:/images/InboxArrowDown.svg"
}
}
}

View File

@@ -1,452 +1,409 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import TeroStyle
GridLayout GridLayout {
{
id: personalData id: personalData
columns: 4
Label function checkPersonalField() {
{ return (firstname.text.trim() && lastname.text.trim() && street.text.trim() && houseno.text.trim() && (postcode.editText.trim() || postcode.currentText.trim()) && (city.editText.trim() || city.currentText.trim()) && birthday.text.trim() && phonenumber.text.trim() && cellphone.text.trim() && email.text.trim() && jobdescription.text.trim() && contractstart.text.trim() && contractend.text.trim() && briefAnrede.text.trim());
text: qsTr("Anrede")
Layout.alignment: Qt.AlignRight
} }
ComboBox function requiredField() {
{ const pf = "Pflichtfeld";
property string name: "title" street.placeholderText = pf;
phonenumber.placeholderText = pf;
cellphone.placeholderText = pf;
email.placeholderText = pf;
jobdescription.placeholderText = pf;
contractstart.placeholderText = pf;
contractend.placeholderText = pf;
briefAnrede.placeholderText = pf;
houseno.placeholderText = pf;
}
columns: 4
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Anrede")
}
ComboBox {
id: title id: title
property string name: "title"
Layout.columnSpan: 3
Layout.fillWidth: true Layout.fillWidth: true
editable: false editable: false
Layout.columnSpan: 3
model: [qsTr("Keine Angabe"), qsTr("Herr"), qsTr("Frau")] model: [qsTr("Keine Angabe"), qsTr("Herr"), qsTr("Frau")]
onCurrentTextChanged:
{ onCurrentTextChanged: {
switch (title.currentIndex) switch (title.currentIndex) {
{
case 1: case 1:
briefAnrede.text = "Sehr geehrter Herr " briefAnrede.text = "Sehr geehrter Herr ";
break break;
case 2: case 2:
briefAnrede.text = "Sehr geehrte Frau " briefAnrede.text = "Sehr geehrte Frau ";
break break;
default: default:
briefAnrede.text = "Guten Tag " briefAnrede.text = "Guten Tag ";
} }
} }
} }
Label Label {
{
text: qsTr("Vorname")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: qsTr("Vorname*")
} }
TextField TextField {
{
property string name: "firstname"
id: firstname id: firstname
Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
onTextChanged: checkFields()
Layout.columnSpan: 3
}
Label
{
text: qsTr("Nachname")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "lastname"
id: lastname
Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
onTextChanged: checkFields()
Layout.columnSpan: 3
}
Label
{
text: qsTr("Straße")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "street"
id: street
Layout.fillWidth: true
placeholderTextColor: "red"
onTextChanged: checkFields()
property string name: "firstname"
Layout.columnSpan: 3
Layout.fillWidth: true
onTextChanged: checkFields()
} }
Label Label {
{
text: qsTr("Nr.")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: qsTr("Nachname*")
} }
TextField TextField {
{ id: lastname
property string name: "houseno"
id: houseno property string name: "lastname"
Layout.columnSpan: 3
Layout.fillWidth: true
onTextChanged: checkFields()
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Straße")
}
TextField {
id: street
property string name: "street"
Layout.fillWidth: true Layout.fillWidth: true
placeholderTextColor: "red" placeholderTextColor: "red"
onTextChanged: checkFields() onTextChanged: checkFields()
validator: RegularExpressionValidator }
{ Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Nr.")
}
TextField {
id: houseno
property string name: "houseno"
Layout.fillWidth: true
placeholderTextColor: "red"
validator: RegularExpressionValidator {
regularExpression: /([0-9a-zA-Z\-]{1,6})/ regularExpression: /([0-9a-zA-Z\-]{1,6})/
} }
onTextChanged: checkFields()
} }
Label Label {
{ Layout.alignment: Qt.AlignRight
text: qsTr("PLZ") text: qsTr("PLZ")
Layout.alignment: Qt.AlignRight
} }
ComboBox {
ComboBox
{
property string name: "postcode"
id: postcode id: postcode
property string name: "postcode"
Layout.fillWidth: true Layout.fillWidth: true
currentIndex: -1
editable: true editable: true
onEditTextChanged: checkFields()
onCurrentTextChanged: checkFields()
onActivated: currentValue
model: address_model model: address_model
popup.height: 300
textRole: "display" textRole: "display"
popup.height: 300
currentIndex: -1 validator: PostcodeValidator {
}
onActivated: currentValue
onCurrentIndexChanged: city.currentIndex = postcode.currentIndex onCurrentIndexChanged: city.currentIndex = postcode.currentIndex
validator: RegularExpressionValidator
{
regularExpression: /([^$][0-9]{1,4})/
}
}
Label
{
text: qsTr("Ort")
Layout.alignment: Qt.AlignRight
}
ComboBox
{
property string name: "city"
id: city
Layout.fillWidth: true
editable: true
onEditTextChanged: checkFields()
onCurrentTextChanged: checkFields() onCurrentTextChanged: checkFields()
model: address_model onEditTextChanged: checkFields()
textRole: "city" }
popup.height: 300 Label {
currentIndex: -1 Layout.alignment: Qt.AlignRight
text: qsTr("Ort")
}
ComboBox {
id: city
property string name: "city"
Layout.fillWidth: true
currentIndex: -1
editable: true
model: address_model
popup.height: 300
textRole: "city"
onCurrentTextChanged: checkFields()
onEditTextChanged: checkFields()
} }
Label Label {
{ Layout.alignment: Qt.AlignRight
text: qsTr("Geburtsname") text: qsTr("Geburtsname")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
} }
TextField TextField {
{
property string name: "birthname"
id: birthname id: birthname
Layout.fillWidth: true
onTextChanged: checkFields() property string name: "birthname"
Layout.columnSpan: 3 Layout.columnSpan: 3
visible: radio.children[1].checked Layout.fillWidth: true
onTextChanged: checkFields()
} }
Label Label {
{
text: qsTr("Geburtsdatum")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked text: qsTr("Geburtsdatum*")
} }
TextField TextField {
{
property string name: "birthday"
id: birthday id: birthday
Layout.fillWidth: true
placeholderText: "Pflichtfeld" property string name: "birthday"
placeholderTextColor: "red"
onTextChanged: checkFields()
Layout.columnSpan: 3 Layout.columnSpan: 3
visible: radio.children[1].checked Layout.fillWidth: true
validator: RegularExpressionValidator
{ validator: RegularExpressionValidator {
regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/ regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/
} }
Keys.onPressed: (event)=>
{
if (event.key !== Qt.Key_Backspace)
{
var len = birthday.length
var bd = birthday.text
if (len === 2 || len === 5) birthday.text = bd + "."
}
}
}
Label Keys.onPressed: event => {
{ if (event.key !== Qt.Key_Backspace) {
text: qsTr("Geburtsort") var len = birthday.length;
Layout.alignment: Qt.AlignRight var bd = birthday.text;
visible: radio.children[1].checked if (len === 2 || len === 5)
birthday.text = bd + ".";
} }
TextField }
{ onTextChanged: checkFields()
property string name: "placeofbirth" }
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Geburtsort*")
}
TextField {
id: placeofbirth id: placeofbirth
Layout.fillWidth: true
placeholderText: "Pflichtfeld" property string name: "placeofbirth"
placeholderTextColor: "red"
onTextChanged: checkFields()
Layout.columnSpan: 3 Layout.columnSpan: 3
visible: radio.children[1].checked Layout.fillWidth: true
onTextChanged: checkFields()
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Telefonnummer")
}
TextField {
id: phonenumber
property string name: "phone"
Layout.columnSpan: 3
Layout.fillWidth: true
placeholderTextColor: "red"
validator: RegularExpressionValidator {
regularExpression: /([+0-9]{1})([0-9]{1,17})/
} }
Label
{
text: qsTr("Telefonnummer")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "phone"
id: phonenumber
Layout.fillWidth: true
placeholderTextColor: "red"
Layout.columnSpan: 3
onTextChanged: checkFields() onTextChanged: checkFields()
validator: RegularExpressionValidator
{
regularExpression: /([+0-9]{1})([0-9]{1,17})/
} }
} Label {
Label Layout.alignment: Qt.AlignRight
{
text: qsTr("Mobil") text: qsTr("Mobil")
Layout.alignment: Qt.AlignRight
} }
TextField TextField {
{
property string name: "mobile"
id: cellphone id: cellphone
property string name: "mobile"
Layout.columnSpan: 3
Layout.fillWidth: true Layout.fillWidth: true
placeholderTextColor: "red" placeholderTextColor: "red"
Layout.columnSpan: 3
onTextChanged: checkFields() validator: RegularExpressionValidator {
validator: RegularExpressionValidator
{
regularExpression: /([+0-9]{1})([0-9]{1,17})/ regularExpression: /([+0-9]{1})([0-9]{1,17})/
} }
}
Label
{
text: qsTr("E-Mail")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "email"
id: email
Layout.fillWidth: true
placeholderTextColor: "red"
Layout.columnSpan: 3
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: qsTr("beispiel@domain.de")
validator: RegularExpressionValidator
{
regularExpression: /([\+!#$%&\*\\/\=?\^_`\.{|}\~\-\_0-9A-Za-z]{1,185})@([0-9A-Za-z\.\-\_]{1,64})\.([a-zA-z]{2,5})/
} }
} Label {
Label
{
text: qsTr("Familienstand")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked text: qsTr("E-Mail")
} }
ComboBox TextField {
{ id: email
property string name: "maritalstatus"
property string name: "email"
Layout.columnSpan: 3
Layout.fillWidth: true
placeholderText: qsTr("beispiel@domain.de")
placeholderTextColor: "red"
validator: EmailAddressValidator {
}
onTextChanged: checkFields()
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Familienstand")
}
ComboBox {
id: maritalstatus id: maritalstatus
property string name: "maritalstatus"
Layout.columnSpan: 3
Layout.fillWidth: true Layout.fillWidth: true
editable: false editable: false
model: [qsTr("ledig"), qsTr("verheiratet"), qsTr("verwitwet"), qsTr("geschieden")] model: [qsTr("ledig"), qsTr("verheiratet"), qsTr("verwitwet"), qsTr("geschieden")]
visible: radio.children[1].checked
Layout.columnSpan: 3
} }
Label Label {
{ Layout.alignment: Qt.AlignRight
text: qsTr("Stundenlohn") text: qsTr("Stundenlohn")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
} }
TextField TextField {
{
property string name: "salary"
id: salary id: salary
Layout.fillWidth: true
visible: radio.children[1].checked property string name: "salary"
placeholderTextColor: "red"
Layout.columnSpan: 3 Layout.columnSpan: 3
Layout.fillWidth: true
placeholderTextColor: "red"
onTextChanged: checkFields() onTextChanged: checkFields()
} }
Label Label {
{ Layout.alignment: Qt.AlignRight
text: qsTr("Jobbeschreibung") text: qsTr("Jobbeschreibung")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
} }
TextField TextField {
{
property string name: "jobdesc"
id: jobdescription id: jobdescription
Layout.fillWidth: true
visible: radio.children[1].checked property string name: "jobdesc"
placeholderTextColor: "red"
Layout.columnSpan: 3 Layout.columnSpan: 3
Layout.fillWidth: true
placeholderTextColor: "red"
onTextChanged: checkFields() onTextChanged: checkFields()
} }
Label Label {
{ Layout.alignment: Qt.AlignRight
text: qsTr("Vertragsbeginn") text: qsTr("Vertragsbeginn")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
} }
TextField TextField {
{
property string name: "contractstart"
id: contractstart id: contractstart
Layout.fillWidth: true
visible: radio.children[1].checked property string name: "contractstart"
placeholderTextColor: "red"
Layout.columnSpan: 3 Layout.columnSpan: 3
onTextChanged: checkFields() Layout.fillWidth: true
validator: RegularExpressionValidator placeholderTextColor: "red"
{
validator: RegularExpressionValidator {
regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/ regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/
} }
Keys.onPressed: (event)=>
{
if (event.key !== Qt.Key_Backspace)
{
var len = contractstart.length
var bd = contractstart.text
if (len === 2 || len === 5) contractstart.text = bd + "."
}
}
}
Label
{
text: qsTr("Vertragsende")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
}
TextField
{
property string name: "contractend"
id: contractend
Layout.fillWidth: true
visible: radio.children[1].checked
placeholderTextColor: "red"
Layout.columnSpan: 3
onTextChanged: checkFields()
validator: RegularExpressionValidator
{
regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/
}
Keys.onPressed: (event)=>
{
if (event.key !== Qt.Key_Backspace)
{
var len = contractend.length
var bd = contractend.text
if (len === 2 || len === 5) contractend.text = bd + "."
}
}
}
Label
{
text: qsTr("Arbeitszeiten Tage")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
}
ComboBox
{
property string name: "workdays"
id: workdays
Layout.fillWidth: true
visible: radio.children[1].checked
model: ["1","2","3","4","5","6","7"]
}
Label
{
text: qsTr("Stunden")
Layout.alignment: Qt.AlignRight
visible: radio.children[1].checked
}
ComboBox
{
property string name: "workhours"
id: workhours
Layout.fillWidth: true
visible: radio.children[1].checked
model: ["1","2","3","4","5","6","7","8"]
}
Label
{
text: qsTr("Briefanrede")
Layout.alignment: Qt.AlignRight
}
TextField
{
property string name: "formofaddress"
id: briefAnrede
Layout.fillWidth: true
placeholderTextColor: "red"
Layout.columnSpan: 3
onTextChanged: checkFields()
}
Item Keys.onPressed: event => {
{ if (event.key !== Qt.Key_Backspace) {
Layout.fillHeight: true var len = contractstart.length;
var bd = contractstart.text;
if (len === 2 || len === 5)
contractstart.text = bd + ".";
}
}
onTextChanged: checkFields()
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Vertragsende")
}
TextField {
id: contractend
property string name: "contractend"
Layout.columnSpan: 3
Layout.fillWidth: true
placeholderTextColor: "red"
validator: RegularExpressionValidator {
regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/
}
Keys.onPressed: event => {
if (event.key !== Qt.Key_Backspace) {
var len = contractend.length;
var bd = contractend.text;
if (len === 2 || len === 5)
contractend.text = bd + ".";
}
}
onTextChanged: checkFields()
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Arbeitszeiten Tage")
}
ComboBox {
id: workdays
property string name: "workdays"
Layout.fillWidth: true
model: ["1", "2", "3", "4", "5", "6", "7"]
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Stunden")
}
ComboBox {
id: workhours
property string name: "workhours"
Layout.fillWidth: true
model: ["1", "2", "3", "4", "5", "6", "7", "8"]
}
Label {
Layout.alignment: Qt.AlignRight
text: qsTr("Briefanrede")
}
TextField {
id: briefAnrede
property string name: "formofaddress"
Layout.columnSpan: 3
Layout.fillWidth: true
placeholderTextColor: "red"
onTextChanged: checkFields()
}
Item {
Layout.columnSpan: 4 Layout.columnSpan: 4
} Layout.fillHeight: true
function checkPersonalField()
{
if (radio.children[0].checked)
{
return (firstname.text.trim() && lastname.text.trim())
}
else
{
return (firstname.text.trim() && lastname.text.trim() && street.text.trim() && houseno.text.trim()
&& (postcode.editText.trim() || postcode.currentText.trim())
&& (city.editText.trim() || city.currentText.trim())
&& birthday.text.trim() && phonenumber.text.trim()
&& cellphone.text.trim() && email.text.trim() && jobdescription.text.trim()
&& contractstart.text.trim() && contractend.text.trim() && briefAnrede.text.trim())
}
}
function requiredField()
{
var pf = (radio.children[1].checked)?"Pflichtfeld":""
street.placeholderText = pf
phonenumber.placeholderText = pf
cellphone.placeholderText = pf
email.placeholderText = pf
jobdescription.placeholderText = pf
contractstart.placeholderText = pf
contractend.placeholderText = pf
briefAnrede.placeholderText = pf
houseno.placeholderText = pf
} }
} }

View File

@@ -0,0 +1,143 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
ColumnLayout {
anchors.fill: parent
spacing: Dimensions.l
RowLayout {
Layout.fillWidth: true
spacing: Dimensions.l
SearchBar {
onSubmitted: (query) => {
applicantModel.searchQuery = query
}
}
QuickFilter {
model: ListModel {
ListElement {
name: "Alle"
selected: true
text: qsTr("Alle")
}
ListElement {
name: "Bewerber"
selected: false
text: qsTr("Bewerber")
}
ListElement {
name: "Mitarbeiter"
selected: false
text: qsTr("Kunde")
}
ListElement {
name: "Erledigt"
selected: false
text: qsTr("Erledigt")
}
}
onSelectedChanged: name => {
employee_model.viewCriterion(name);
}
}
Button {
Layout.alignment: Qt.AlignRight
flat: true
icon.source: "qrc:/images/PlusCircle.svg"
text: qsTr("Bewerber Hinzufügen")
onClicked: contentStack.push("AddApplicant.qml")
}
Button {
Layout.alignment: Qt.AlignRight
flat: true
icon.source: "qrc:/images/PlusCircle.svg"
text: qsTr("Mitarbeiter Hinzufügen")
onClicked: contentStack.push("AddEmployee.qml")
}
}
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 2
HorizontalHeaderView {
movableColumns: true
syncView: employeesTable
delegate: Rectangle {
Layout.fillWidth: true
color: Colors.primary
implicitHeight: 33
implicitWidth: 1
Text {
color: Colors.primaryContrast
elide: Text.ElideRight
font: Typography.smallBold
height: parent.height
horizontalAlignment: Text.AlignLeft
padding: Dimensions.s
text: model.display
verticalAlignment: Text.AlignVCenter
width: parent.width
}
}
}
TableView {
id: employeesTable
Layout.fillHeight: true
Layout.fillWidth: true
columnSpacing: 2
model: applicantModel
resizableColumns: true
rowSpacing: 2
selectionBehavior: TableView.SelectRows
z: 1
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}
delegate: Rectangle {
required property bool selected
color: selected ? Colors.primaryHighlight : Colors.transparent
implicitHeight: 33
implicitWidth: employeesTable.width / employeesTable.columns
Text {
color: Colors.foreground
elide: Text.ElideRight
font: Typography.small
height: parent.height
padding: Dimensions.s
text: (model.display === null || model.display === undefined) ? "" : model.display
verticalAlignment: Text.AlignVCenter
width: parent.width
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
contentStack.push("EmployeeDetails.qml", { row });
}
onEntered: {
employeesTable.selectionModel.select(employeesTable.model.index(row, 0), ItemSelectionModel.SelectCurrent | ItemSelectionModel.Rows);
}
}
}
selectionModel: ItemSelectionModel {
model: employeesTable.model
}
}
}
}

1
Gui/Employees/qmldir Normal file
View File

@@ -0,0 +1 @@
module Employees

View File

@@ -1,224 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
Item
{
property var availableFilters: ["Name", "Adresse", "PLZ", "Ort", "Status"]
SearchBar
{
id:searchBar
anchors.margins: 9
}
Button
{
id: addEmployeeBtn
icon.source: "qrc:/images/addbusiness.svg"
icon.color: "olive"
anchors.right: parent.right
flat: true
onClicked: appLoader.source = "AddApplicant.qml"
}
ButtonGroup
{
id: criterion
// buttons: criterion.children
onClicked:
{
viewEmployees(criterion.checkedButton.text)
}
}
ColumnLayout
{
id: tableParent
clip: true
anchors
{
top: searchBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
RowLayout
{
id: viewCriterion
RadioButton
{
//id: showAll
checked: true
text: qsTr("Alle")
ButtonGroup.group: criterion
//onClicked: viewEmployees(showAll)
}
RadioButton
{
//id: showApplicant
text: qsTr("Bewerber")
ButtonGroup.group: criterion
//onClicked: viewEmployees(showApplicant)
}
RadioButton
{
//id: showEmployee
text: qsTr("Mitarbeiter")
ButtonGroup.group: criterion
//onClicked: viewEmployees(showEmployee)
}
CheckBox
{
id: showEveryone
text: qsTr("Alle Stati")
checked: true
onClicked: viewEmployees(criterion.checkedButton.text)
onCheckedChanged:
{
showFired.checked = false
showProcessed.checked = false
}
}
CheckBox
{
id: showProcessed
text: qsTr("Erledigt")
enabled: !showEveryone.checked
checked: false
onClicked:
{
showFired.checked = false
viewEmployees(criterion.checkedButton.text)
}
}
CheckBox
{
id: showFired
text: qsTr("Ausgeschieden")
enabled: !showEveryone.checked
checked: false
onClicked:
{
showProcessed.checked = false
viewEmployees(criterion.checkedButton.text)
}
}
}
HorizontalHeaderView
{
id: employeeTableHeader
Layout.fillWidth: true
syncView: appliEmpTable
implicitHeight: 40
movableColumns: true //@disable-check M16
delegate: Rectangle
{
color: addEmployeeBtn.palette.alternateBase
border.color: palette.base
implicitHeight: 40
Layout.fillWidth: true
implicitWidth: 1
Text
{
text: model.display
elide: Text.ElideRight
width: parent.width
height: parent.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: palette.text
}
}
}
TableView
{
id: appliEmpTable
//Layout.fillHeight: true
height: tableParent.height - (viewCriterion.height + employeeTableHeader.height)
Layout.fillWidth: true
columnSpacing: 1
rowSpacing: 2
alternatingRows: true
resizableColumns: true
model: employee_model
selectionBehavior: TableView.SelectRows
ScrollBar.vertical: ScrollBar
{
policy: appliEmpTable.contentHeight > appliEmpTable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
}
selectionModel: ItemSelectionModel
{
id: selModel
model: appliEmpTable.model
}
delegate:Rectangle
{
required property bool selected
required property bool current
implicitWidth: appliEmpTable.width / appliEmpTable.columns
implicitHeight: 25
color: selected
? addEmployeeBtn.palette.highlight //palette.highlight
: (appliEmpTable.alternatingRows && row % 2 !== 0
? addEmployeeBtn.palette.base // palette.base
: addEmployeeBtn.palette.alternateBase) //palette.alternateBase)
Text
{
text: (model.display === null || model.display === undefined)? "": model.display
elide: Text.ElideRight
width: parent.width
height: parent.height
verticalAlignment: Text.AlignVCenter
leftPadding: 9 //@d isable-check M16
color: palette.text
}
MouseArea
{
id: mouseArea
property bool hovered:false
anchors.fill: parent
hoverEnabled: true
onDoubleClicked:
{
employeesStack.push("EmployeeDetails.qml", {selectedEmployee: row});
}
onEntered:
{
appliEmpTable.selectionModel.select(appliEmpTable.model.index(row, 0), ItemSelectionModel.SelectCurrent | ItemSelectionModel.Rows)
}
}
}
}
Item
{
Layout.fillWidth: true
}
}
function viewEmployees(criterion)
{
employee_model.viewCriterion(criterion, showProcessed.checked, showFired.checked, showEveryone.checked)
}
Component.onCompleted: employeesStack.pop()
}

View File

@@ -77,7 +77,7 @@ Item
title: qsTr("Wiederherstellen") title: qsTr("Wiederherstellen")
buttons: MessageDialog.Yes | MessageDialog.No buttons: MessageDialog.Yes | MessageDialog.No
onAccepted: recoveryPaswordDialog.open() onAccepted: recoveryPaswordDialog.open()
onRejected: gotoLogin() onRejected: contentStack.replace("LoginSreen.qml")
} }
MessageDialog MessageDialog
@@ -147,7 +147,7 @@ Item
if (!adminAvailable) config.saveRecoveryKey(saveRecoveryDialog.currentFile, recpass) if (!adminAvailable) config.saveRecoveryKey(saveRecoveryDialog.currentFile, recpass)
else config.getRecoveryKey(saveRecoveryDialog.currentFile, recpass) else config.getRecoveryKey(saveRecoveryDialog.currentFile, recpass)
gotoLogin() contentStack.replace("LoginSreen.qml")
} }
onRejected: onRejected:

View File

@@ -4,20 +4,19 @@ import QtQuick.Controls
import QtQuick.Dialogs import QtQuick.Dialogs
import QtQuick.Layouts import QtQuick.Layouts
Item { Item {
property string recpass: "" property string recpass: ""
function dbConnectionFailed(msg) { function dbConnectionFailed(msg) {
oschkar.notificationBox.informativeText = msg; notifications.notificationDialog.informativeText = msg;
oschkar.notificationBox.text = "Verbindung zum Datenbankserver verloren"; notifications.notificationDialog.text = "Verbindung zum Datenbankserver verloren";
oschkar.notificationBox.open(); notifications.notificationDialog.open();
} }
function getEncryptionKey() { function getEncryptionKey() {
recoveryPaswordDialog.open(); recoveryPaswordDialog.open();
} }
function loggedin() { function loggedin() {
appLoader.source = "Dashboard.qml"; contentStack.replace("Dashboard.qml");
} }
anchors.fill: parent anchors.fill: parent
@@ -32,16 +31,15 @@ Item {
} }
ColumnLayout { ColumnLayout {
spacing: Dimensions.m
anchors.centerIn: parent anchors.centerIn: parent
spacing: Dimensions.m
Label { Label {
font: Typography.h1
text: qsTr("Login")
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Dimensions.l Layout.bottomMargin: Dimensions.l
font: Typography.h1
text: qsTr("Login")
} }
Field { Field {
label: qsTr("Benutzername") label: qsTr("Benutzername")
@@ -78,11 +76,11 @@ Item {
} }
} }
} }
Button { Button {
Layout.topMargin: Dimensions.m Layout.topMargin: Dimensions.m
icon.source: "qrc:/images/ArrowRightEndOnRectangle.svg"
implicitWidth: parent.width
text: qsTr("Login") text: qsTr("Login")
width: parent.width
onClicked: { onClicked: {
if (benutzerName.text.trim() && passwort.text.trim()) if (benutzerName.text.trim() && passwort.text.trim())
@@ -133,8 +131,7 @@ Item {
onRejected: quit() onRejected: quit()
} }
Notifications { Notifications {
id: oschkar id: notifications
} }
} }
} }

120
Gui/Navigation.qml Normal file
View File

@@ -0,0 +1,120 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ColumnLayout {
property bool onSubPage: false
height: parent.height
spacing: 0
z: 3
Component.onCompleted: {
onSubPage = Qt.binding(() => contentStack.depth > 1);
}
anchors {
left: parent.left
top: parent.top
}
ButtonGroup {
id: mainNav
}
BarButton {
ButtonGroup.group: mainNav
icon.source: "qrc:/images/Square3Stack3D-Outline.svg"
target: "/Gui/Dashboard.qml"
text: qsTr("Dashboard")
visible: !onSubPage
}
BarButton {
ButtonGroup.group: mainNav
icon.source: "qrc:/images/UserGroup-Outline.svg"
target: "/Gui/CustomersTable.qml"
text: qsTr("Kunden")
visible: !onSubPage
}
BarButton {
ButtonGroup.group: mainNav
icon.source: "qrc:/images/BuildingOffice2-Outline.svg"
target: "/Gui/ObjectsTable.qml"
text: qsTr("Objekt")
visible: !onSubPage
}
BarButton {
ButtonGroup.group: mainNav
icon.source: "qrc:/images/Identification-Outline.svg"
target: "/Gui/Employees/EmployeesTable.qml"
text: qsTr("Mitarbeiter")
visible: !onSubPage
}
BarButton {
ButtonGroup.group: mainNav
icon.source: "qrc:/images/RectangleStack-Outline.svg"
target: "/Gui/OffersTable.qml"
text: qsTr("Angebote")
visible: !onSubPage
}
BarButton {
ButtonGroup.group: mainNav
icon.source: "qrc:/images/Wallet-Outline.svg"
text: qsTr("Abrechnung")
visible: !onSubPage
}
BarButton {
icon.source: "qrc:/images/ArrowLeftCircle-Outline.svg"
text: qsTr("Zurück")
visible: onSubPage
checkable: false
onClicked: contentStack.pop();
}
Item {
Layout.fillHeight: true
}
BarButton {
checkable: false
icon.source: "qrc:/images/Bars3.svg"
onClicked: mainMenu.open()
Menu {
id: mainMenu
MenuItem {
text: qsTr("Einstellungen")
onTriggered: {
// TODO: Check if logged-in user is admin first!!
contentStack.push("PyqcrmConf.qml");
}
}
MenuSeparator {
}
MenuItem {
text: qsTr("Als PDF exportieren")
}
MenuSeparator {
}
MenuItem {
text: qsTr("Drucken")
}
MenuItem {
text: qsTr("Erweiterter Druck")
onTriggered: printerDialog.show()
}
MenuSeparator {
}
MenuItem {
text: qsTr("Über PYQCRM")
onTriggered: readMeWin.show()
}
}
}
}

View File

@@ -1,74 +1,27 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
Rectangle ColumnLayout {
{
anchors.fill: parent
color: "slateblue"
Rectangle
{
id: info
anchors.horizontalCenter: parent.horizontalCenter
color: "slateblue"
implicitHeight: 55
implicitWidth: parent.width / 4
y: parent.height / 4
Text
{
anchors.centerIn: parent anchors.centerIn: parent
spacing: Dimensions.s
height: implicitHeight
H1 {
Layout.alignment: Qt.AlignCenter
text: qsTr("Keine Verbindung zur Datenbank!") text: qsTr("Keine Verbindung zur Datenbank!")
color: "moccasin" color: Colors.foreground
font.bold: true
font.pixelSize: 45
} }
H2 {
Layout.alignment: Qt.AlignCenter
text: qsTr("Programm kann nicht starten…")
color: Colors.foreground
} }
Button {
Rectangle Layout.topMargin: Dimensions.l
{ Layout.alignment: Qt.AlignCenter
id: nostart
anchors.top: info.bottom
color: "slateblue"
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: 55
implicitWidth: parent.width / 4
Text
{
text: qsTr("Programm kann nicht starten..")
color: "moccasin"
anchors.centerIn: parent
font.bold: true
font.pixelSize: 45
}
}
Rectangle
{
anchors.top: nostart.bottom
anchors.topMargin: 25
anchors.horizontalCenter: parent.horizontalCenter
color: "slateblue"
implicitHeight: 55
implicitWidth: parent.width / 4
Button
{
width: parent.width
height: 75
Text
{
text: qsTr("Beenden") text: qsTr("Beenden")
color: "moccasin"
anchors.centerIn: parent
font.bold: true
font.pixelSize: 45
}
anchors.centerIn: parent
background: Rectangle
{
color: "dodgerblue"
radius: 50
}
onClicked: Qt.quit() onClicked: Qt.quit()
} }
}
} }

View File

@@ -3,8 +3,6 @@ import QtQuick.Dialogs
Item Item
{ {
id: oschkar
property alias notificationBox: notificationDialog
MessageDialog MessageDialog
{ {
id: notificationDialog id: notificationDialog

View File

@@ -35,71 +35,45 @@ GridLayout
} }
Label Label
{ {
text: qsTr("Vorname") text: qsTr("Vorname*")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
TextField TextField
{ {
id: firstname id: firstname
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
// onTextChanged: checkContactFields() // onTextChanged: checkContactFields()
} }
Label Label
{ {
text: qsTr("Nachname") text: qsTr("Nachname*")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
TextField TextField
{ {
id: lastname id: lastname
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
text: qsTr("Telefonnummer") text: mobile.text ? qsTr("Telefonnummer") : qsTr("Telefonnummer*")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
TextField TextField
{ {
id: phonenumber id: phonenumber
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: mobile.text ? "" : "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
text: qsTr("Mobil") text: phonenumber.text ? qsTr("Mobil") : qsTr("Mobil*")
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
TextField TextField
{ {
id: mobile id: mobile
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: phonenumber.text ? "" : "Pflichtfeld"
placeholderTextColor: "red"
} }
// Label
// {
// text: qsTr("Position")
// Layout.alignment: Qt.AlignRight
// }
// TextField
// {
// id: posizion
// Layout.fillWidth: true
// placeholderText: "Pflichtfeld"
// placeholderTextColor: "red"
// }
RowLayout RowLayout
{ {

View File

@@ -16,7 +16,7 @@ Item
Button Button
{ {
text: qsTr("Zurück zu den Objekten") text: qsTr("Zurück zu den Objekten")
onClicked: objectsStack.pop() onClicked: contentStack.pop()
} }
} }

View File

@@ -1,22 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
Item {
property var availableFilters: [""]
StackView
{
id: objectsStack
anchors.fill: parent
initialItem: "ObjectsTable.qml"
anchors.topMargin: Dimensions.m
}
}

View File

@@ -27,7 +27,7 @@ GridLayout
Label Label
{ {
text: qsTr("Straße") text: qsTr("Straße*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -38,12 +38,10 @@ GridLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
text: qsTr("Nr.") text: qsTr("Nr.*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -54,13 +52,11 @@ GridLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
text: qsTr("PLZ") text: qsTr("PLZ*")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
} }
@@ -70,8 +66,6 @@ GridLayout
id: postcode id: postcode
Layout.fillWidth: true Layout.fillWidth: true
onTextChanged: checkFields() onTextChanged: checkFields()
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
} }
Label Label
{ {
@@ -152,8 +146,7 @@ GridLayout
} }
TextField TextField
{ {id: gesamtnetto
id: gesamtnetto
Layout.fillWidth: true Layout.fillWidth: true
Layout.columnSpan: 3 Layout.columnSpan: 3
} }

View File

@@ -2,43 +2,101 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.qmlmodels import Qt.labs.qmlmodels
Item
{
SearchBar ColumnLayout
{
property var availableFilters: [""]
anchors.fill: parent
spacing: Dimensions.l
function viewCriterion(criterion)
{ {
id:searchBar business_model.viewCriterion(criterion.text);
}
function onObjectContactAdded(added)
{
console.log(added)
if (added) object_model.viewCriterion("")
} }
Component.onCompleted:
{
contact_model.objectContactAdded.connect(onObjectContactAdded)
contentStack.pop()
}
RowLayout
{
Layout.fillWidth: true
spacing: Dimensions.l
SearchBar
{
}
QuickFilter {
onSelectedChanged: (name) => {
business_model.viewCriterion(name)
}
model: ListModel {
ListElement {
name: "Alle"
text: qsTr("Alle")
selected: true
}
ListElement {
name: "Interessent"
text: qsTr("Interessent")
selected: false
}
ListElement {
name: "Kunde"
text: qsTr("Kunde")
selected: false
}
ListElement {
name: "Lieferant"
text: qsTr("Lieferant")
selected: false
}
ListElement {
name: "Erledigt"
text: qsTr("Erledigt")
selected: false
}
}
}
Button Button
{ {
id: addObjectBtn id: addObjectBtn
icon.source: "qrc:/images/addbusiness.svg" icon.source: "qrc:/images/PlusCircle.svg"
icon.color: "olive" text: qsTr("Objekt Hinzufügen")
anchors.right: parent.right Layout.alignment: Qt.AlignRight
flat: true onClicked: contentStack.push("AddObject.qml")
onClicked: appLoader.source = "AddObject.qml" }
} }
ColumnLayout ColumnLayout
{ {
id: tableColumn id: tableColumn
Layout.fillWidth: true
Layout.fillHeight: true
Layout.verticalStretchFactor: 1
clip: true clip: true
anchors // anchors
{ // {
top: searchBar.bottom // top: searchBar.bottom
bottom: parent.bottom // bottom: parent.bottom
left: parent.left // left: parent.left
right: parent.right // right: parent.right
topMargin: 15 // topMargin: 15
} // }
HorizontalHeaderView HorizontalHeaderView
{ {
id: horizontalHeaderview id: horizontalHeaderview
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: 40 implicitHeight: 40
//visible: false
movableColumns: true //@disable-check M16 movableColumns: true //@disable-check M16
syncView: objectTable syncView: objectTable
@@ -65,8 +123,9 @@ Item
{ {
property real newWidth: 0 property real newWidth: 0
id: objectTable id: objectTable
z: 0 z: 1
height: tableColumn.height - horizontalHeaderview.height // height: tableColumn.height - horizontalHeaderview.height
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
columnSpacing: 1 columnSpacing: 1
rowSpacing: 2 rowSpacing: 2
@@ -115,7 +174,7 @@ Item
hoverEnabled: true hoverEnabled: true
onDoubleClicked: onDoubleClicked:
{ {
objectsStack.push("ObjectDetails.qml", {selectedObject: row}); contentStack.push("ObjectDetails.qml", {selectedObject: row});
} }
onEntered: onEntered:
{ {
@@ -123,34 +182,10 @@ Item
} }
} }
} }
// onContentWidthChanged:
// {
// redrawTable.start()
// }
} }
Item
{
Layout.fillWidth: true
} }
} Item {
// function viewCriterion(criterion) Layout.fillHeight: true
// {
// object_model.viewCriterion(criterion.text)
// }
Component.onCompleted:
{
contact_model.objectContactAdded.connect(onObjectContactAdded)
objectsStack.pop()
}
function onObjectContactAdded(added)
{
console.log(added)
if (added) object_model.viewCriterion("")
} }
} }

View File

@@ -1,17 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
Item
{
StackView
{
id: employeesStack
anchors.fill: parent
initialItem: "OffersTable.qml"
anchors.topMargin: Dimensions.m
}
}

View File

@@ -3,12 +3,64 @@ import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.qmlmodels import Qt.labs.qmlmodels
Item ColumnLayout {
{ function viewOffers(criterion) {
property var availableFilters: [] //offer_model.viewCriterion(criterion)
}
SearchBar anchors.fill: parent
{ spacing: Dimensions.l
id:searchBar
RowLayout {
Layout.fillWidth: true
spacing: Dimensions.l
SearchBar {
id: searchBar
}
QuickFilter {
model: ListModel {
ListElement {
name: "Alle"
selected: true
text: qsTr("Alle")
}
ListElement {
name: "Offen"
selected: false
text: qsTr("Offen")
}
ListElement {
name: "Abgeschlossen"
selected: false
text: qsTr("Abgeschlossen")
}
ListElement {
name: "Erledigt"
selected: false
text: qsTr("Erledigt")
}
}
onSelectedChanged: name => {
business_model.viewCriterion(name);
}
}
Button {
id: addOfferBtn
Layout.alignment: Qt.AlignRight
flat: true
icon.source: "qrc:/images/PlusCircle.svg"
text: qsTr("Angebote Hinzufügen")
onClicked: contentStack.push("AddOffer.qml")
}
}
Item {
id: spacer
Layout.fillHeight: true
} }
} }

View File

@@ -101,7 +101,7 @@ Item
Button Button
{ {
text: qsTr("Ablehnen") text: qsTr("Ablehnen")
onClicked: appLoader.source = "Dashboard.qml" onClicked: contentStack.pop()
} }
Button Button

View File

@@ -1,81 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RowLayout
{
id: searchBar
TextField
{
id: searchField
placeholderText: qsTr("Suche")
leftPadding: 3
rightPadding: 3
Layout.preferredWidth: 300
Button
{
icon.source: "qrc:/images/search.svg"
icon.color: "olive"
x: parent.x + parent.width - width
height: parent.height
flat: true
}
Button
{
id: filterBtn
icon.source: "qrc:/images/filter.svg"
icon.color: "olive"
x: parent.x + parent.width
height: searchField.height
flat: true
onClicked: filterPopup.open()
}
}
Popup
{
id: filterPopup
x: filterBtn.x + filterBtn.width
y: filterBtn.y
width: 100
height: 150
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
contentItem: Item
{
ColumnLayout
{
anchors.fill: parent
//id: filterContent
Repeater
{
model: availableFilters
CheckBox
{
text: model.modelData
onClicked:
{
setFilter(text, checkState)
}
}
}
}
}
}
function setFilter(filter,activated)
{
console.log(filter)
console.log(activated)
}
}

View File

@@ -1,271 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ColumnLayout
{
id: topBar
spacing: 0
height: parent.height
anchors
{
top: parent.top
left: parent.left
}
ButtonGroup
{
id: buttonBar
}
BarButton
{
id: dashBoard
flat: true
text: qsTr("Dashboard")
implicitWidth: 90
implicitHeight: 90
Layout.margins: 3
Layout.topMargin: Dimensions.s
icon.source: "qrc:/images/dash.svg"
ButtonGroup.group: buttonBar
// background: Rectangle
// {
// id: dashiBackie
// border.width: dashBoard.activeFocus ? 2 : 1
// border.color: "#888"
// radius: 4
// gradient: Gradient
// {
// GradientStop { position: 0 ; color: dashBoard.pressed ? "#000" : "#001" }
// GradientStop { position: 1 ; color: dashBoard.pressed ? "#100" : "#000" }
// }
// }
onClicked:
{
appLoader.source = "Dashboard.qml"
// dashiBackie.border.width = 2
// kundiBackie.border.width = 1
// mitoBackie.border.width = 1
// invoBackie.border.width = 1
// objBackie.border.width = 1
}
}
BarButton
{
id: kunden
flat: true
text: qsTr("Kunden")
implicitWidth: 90
implicitHeight: 90
Layout.margins: 3
icon.source: "qrc:/images/customer.svg"
ButtonGroup.group: buttonBar
// background: Rectangle
// {
// id: kundiBackie
// border.width: kunden.activeFocus ? 2 : 1
// border.color: "#888"
// radius: 4
// gradient: Gradient
// {
// GradientStop { position: 0 ; color: kunden.pressed ? "#000" : "#001" }
// GradientStop { position: 1 ; color: kunden.pressed ? "#100" : "#000" }
// }
// }
onClicked:
{
// TODO: here we should call the model
appLoader.source = "CustomerTable.qml"
// kundiBackie.border.width = 2
// dashiBackie.border.width = 1
// mitoBackie.border.width = 1
// invoBackie.border.width = 1
// objBackie.border.width = 1
}
}
BarButton
{
id: objekt
flat: true
text: qsTr("Objekt")
implicitWidth: 90
implicitHeight: 90
Layout.margins: 3
icon.source: "qrc:/images/object.svg"
ButtonGroup.group: buttonBar
// background: Rectangle
// {
// id: objBackie
// border.width: objekt.activeFocus ? 2 : 1
// border.color: "#888"
// radius: 4
// gradient: Gradient
// {
// GradientStop { position: 0 ; color: objekt.pressed ? "#000" : "#001" }
// GradientStop { position: 1 ; color: objekt.pressed ? "#100" : "#000" }
// }
// }
onClicked:
{
appLoader.source = "ObjectTable.qml"
// objBackie.border.width = 2
// dashiBackie.border.width = 1
// kundiBackie.border.width = 1
// mitoBackie.border.width = 1
// invoBackie.border.width = 1
}
}
BarButton
{
id: mitarbeiter
flat: true
text: qsTr("Mitarbeiter")
implicitWidth: 90
implicitHeight: 90
Layout.margins: 3
icon.source: "qrc:/images/employee.svg"
ButtonGroup.group: buttonBar
// background: Rectangle
// {
// id: mitoBackie
// border.width: mitarbeiter.activeFocus ? 2 : 1
// border.color: "#888"
// radius: 4
// gradient: Gradient
// {
// GradientStop { position: 0 ; color: mitarbeiter.pressed ? "#000" : "#001" }
// GradientStop { position: 1 ; color: mitarbeiter.pressed ? "#100" : "#000" }
// }
// }
onClicked:
{
appLoader.source = "EmployeeTable.qml"
// mitoBackie.border.width = 2
// dashiBackie.border.width = 1
// kundiBackie.border.width = 1
// invoBackie.border.width = 1
// objBackie.border.width = 1
}
}
BarButton
{
id: offers
flat: true
text: qsTr("Angebote")
implicitWidth: 90
implicitHeight: 90
Layout.margins: 3
icon.source: "qrc:/images/offer.svg"
ButtonGroup.group: buttonBar
// background: Rectangle
// {
// id: offersBackie
// border.width: offers.activeFocus ? 2 : 1
// border.color: "#888"
// radius: 4
// gradient: Gradient
// {
// GradientStop { position: 0 ; color: offers.pressed ? "#000" : "#001" }
// GradientStop { position: 1 ; color: offers.pressed ? "#100" : "#000" }
// }
// }
onClicked:
{
appLoader.source = "OfferTable.qml"
// mitoBackie.border.width = 2
// dashiBackie.border.width = 1
// kundiBackie.border.width = 1
// invoBackie.border.width = 1
// objBackie.border.width = 1
// offersBackie.border.width = 1
}
}
BarButton
{
id: abrechnung
flat: true
text: qsTr("Abrechnung")
implicitWidth: 90
implicitHeight: 90
Layout.margins: 3
icon.source: "qrc:/images/invoice.svg"
ButtonGroup.group: buttonBar
// background: Rectangle
// {
// id: invoBackie
// border.width: abrechnung.activeFocus ? 2 : 1
// border.color: "#888"
// radius: 4
// gradient: Gradient
// {
// GradientStop { position: 0 ; color: abrechnung.pressed ? "#000" : "#001" }
// GradientStop { position: 1 ; color: abrechnung.pressed ? "#100" : "#000" }
// }
// }
}
Item
{
id: hspacer
Layout.fillHeight: true
}
BarButton
{
id: atajos
icon.source: "qrc:/images/menu.svg"
implicitWidth: 90
implicitHeight: 90
checkable: false
flat: true
Layout.bottomMargin: Dimensions.s
onClicked: mainMenu.open()
Menu {
id: mainMenu
MenuItem
{
//text: qsTr("Benutzer-Verwaltung")
//onTriggered: appLoader.source = "UsersPage.qml"
text: qsTr("Einstellungen")
onTriggered:
{
// TODO: Check if logged-in user is admin first!!
appLoader.source = "PyqcrmConf.qml"
}
}
MenuSeparator {}
MenuItem { text: qsTr("Als PDF exportieren") }
MenuSeparator {}
MenuItem { text: qsTr("Drucken") }
MenuItem
{
text: qsTr("Erweiterter Druck")
onTriggered: printerDialog.show()
}
MenuSeparator {}
MenuItem
{
text: qsTr("Über PYQCRM")
onTriggered: readMeWin.show()
}
}
}
}

View File

@@ -97,7 +97,7 @@ Item
title: qsTr("Wiederherstellen") title: qsTr("Wiederherstellen")
buttons: MessageDialog.Yes | MessageDialog.No buttons: MessageDialog.Yes | MessageDialog.No
onAccepted: recoveryPasswordDialog.open() onAccepted: recoveryPasswordDialog.open()
onRejected: gotoLogin() onRejected: contentStack.replace("LoginSreen.qml")
} }
MessageDialog MessageDialog

View File

@@ -4,165 +4,142 @@ import QtQuick.Controls
import QtQuick.Dialogs import QtQuick.Dialogs
import QtCore import QtCore
ApplicationWindow ApplicationWindow {
{
id: appWindow id: appWindow
width: Screen.width * .75
height: Screen.height * .85
visible: true
title: "TERO Personal"
font: Typography.body
color: Colors.mantle
property string confile: "" property string confile: ""
property alias settingsFileDialog: settingsFiledialog property alias settingsFileDialog: settingsFiledialog
function showWindow(why) {
TopBar if (why === 3) {
{ systray.setVisible(false);
appWindow.show();
id:topBar }
visible: bad_config || !db_con ? false: true
} }
PrinterDialog font: Typography.body
{ height: Screen.height * .85
palette.window: Colors.mantle
palette.placeholderText: Colors.interactive
palette.text: Colors.foreground
title: "TERO Personal"
visible: true
width: Screen.width * .75
Component.onCompleted: {
systray.activated.connect(showWindow);
if (bad_config) {
importDialog.open();
} else {
if (db_con)
contentStack.replace("LoginScreen.qml")
else
contentStack.replace("NoDbConnection.qml");
}
}
onClosing: close => {
if (false) {
console.log("Main window closed!! Was soll ich tun? kann ich mich beenden?!");
}
}
onVisibilityChanged: {
if (appWindow.visibility === Window.Minimized && config.systray()) {
systray.setVisible(true);
appWindow.hide();
}
}
onWindowStateChanged: windowState => {
if (windowState !== Qt.WindowMinimized) {
systray.setVisible(false);
appWindow.show();
}
}
Navigation {
id: navigation
visible: !(bad_config || !db_con)
}
PrinterDialog {
id: printerDialog id: printerDialog
}
ReadMe }
{ ReadMe {
id: readMeWin id: readMeWin
}
Item }
{ Item {
id: mainView id: mainView
} }
Loader Rectangle {
{ id: contentBackground
id: appLoader anchors {
bottom: parent.bottom
anchors left: navigation.visible ? navigation.right : parent.left
{
left: topBar.right
right: parent.right right: parent.right
top: parent.top top: parent.top
bottom: parent.bottom }
margins: Dimensions.s color: Colors.background
} }
property alias window: appWindow StackView {
id: contentStack
anchors {
fill: contentBackground
margins: Dimensions.l
}
} }
Dialog {
Dialog
{
id: importDialog id: importDialog
modal: true
anchors.centerIn: parent anchors.centerIn: parent
modal: true
standardButtons: Dialog.Yes | Dialog.No standardButtons: Dialog.Yes | Dialog.No
onAccepted: settingsFiledialog.open()
onRejected: appLoader.source= "Firststart.qml"
title: qsTr("Einstellungen importieren") title: qsTr("Einstellungen importieren")
onAccepted: settingsFiledialog.open()
onRejected: contentStack.replace("Firststart.qml")
} }
FileDialog {
FileDialog
{
id: settingsFiledialog id: settingsFiledialog
title: qsTr("PYQCRM Einstellungen")
currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0] currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0]
modality: "ApplicationModal" modality: "ApplicationModal"
nameFilters: [qsTr("PYQCRM Einstellungen (*.pyqrec)")] nameFilters: [qsTr("PYQCRM Einstellungen (*.pyqrec)")]
onAccepted:
{
exportFilePassword.open()
confile = selectedFile
}
}
Dialog
{
id: exportFilePassword
modal: true
title: qsTr("PYQCRM Einstellungen") title: qsTr("PYQCRM Einstellungen")
onAccepted: {
exportFilePassword.open();
confile = selectedFile;
}
}
Dialog {
id: exportFilePassword
anchors.centerIn: parent anchors.centerIn: parent
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel standardButtons: Dialog.Ok | Dialog.Cancel
title: qsTr("PYQCRM Einstellungen")
onAccepted: config.importConfig(confile, exportPasswordInput.text) onAccepted: config.importConfig(confile, exportPasswordInput.text)
ColumnLayout
{ ColumnLayout {
RowLayout RowLayout {
{ Label {
Label
{
text: qsTr("Passwort eingeben:") text: qsTr("Passwort eingeben:")
} }
TextField {
TextField
{
id: exportPasswordInput id: exportPasswordInput
echoMode: TextInput.Password echoMode: TextInput.Password
implicitWidth: 300 implicitWidth: 300
} }
} }
} }
} }
Component.onCompleted:
{
config.configurationReady.connect(goToLogin)
systray.activated.connect(showWindow)
if(bad_config)
{
importDialog.open()
}
else
{
if (db_con) appLoader.source= "LoginScreen.qml"
else appLoader.source= "NoDbConnection.qml"
}
}
function showWindow(why)
{
if (why === 3)
{
systray.setVisible(false)
appWindow.show()
}
}
onVisibilityChanged:
{
if (appWindow.visibility === Window.Minimized && config.systray())
{
systray.setVisible(true)
appWindow.hide()
}
}
onWindowStateChanged: (windowState) =>
{
if (windowState !== Qt.WindowMinimized)
{
systray.setVisible(false)
appWindow.show()
}
}
onClosing: (close) =>
{
if (false)
{
console.log("Main window closed!! Was soll ich tun? kann ich mich beenden?!")
}
}
function goToLogin()
{
appLoader.source= "LoginScreen.qml"
topBar.visible = true
}
} }

View File

@@ -1,2 +1,2 @@
module gui module gui
TopBar 1.0 TopBar.qml Navigation 1.0 Navigation.qml

View File

@@ -6,82 +6,56 @@ import QtQuick.Templates as T
T.ToolButton { T.ToolButton {
id: control id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, property string target
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
checkable: true checkable: true
icon.color: Colors.foreground icon.color: Colors.foreground
icon.height: 36 icon.height: 36
icon.width: 36 icon.width: 36
implicitHeight: 90
implicitWidth: 100
topPadding: 20 topPadding: 20
// horizontalPadding: padding + 2
// spacing: 6
// display: AbstractButton.TextUnderIcon
// icon.color: control.checked || control.highlighted ? control.palette.brightText :
// control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText
contentItem: Column
{
IconLabel
{
contentItem: Column {
readonly property color color: control.checked ? Colors.primaryShade : control.hovered ? Colors.primary : Colors.foreground
IconLabel {
color: parent.color
icon.color: parent.color
icon.height: control.icon.height
icon.source: control.icon.source
icon.width: control.icon.width
x: parent.width * .5 - width * .5 x: parent.width * .5 - width * .5
// height: icon.height
// width: icon.width
icon: control.icon
// display: control.display
} }
Label Label {
{ color: parent.color
font: Typography.smaller
text: control.text text: control.text
font: Typography.dash
color: Colors.foreground
x: parent.width * .5 - width * .5 x: parent.width * .5 - width * .5
}
} }
// spacing: control.spacing Rectangle {
// mirrored: control.mirrored anchors.bottom: parent.bottom
// display: control.TextUnderIcon anchors.left: parent.left
anchors.top: parent.top
// icon: control.icon color: control.checked ? Colors.primaryShade : Colors.primary
// text: control.text
// font: control.font
// color: control.checked || control.highlighted ? control.palette.brightText :
// control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText
}
background: Rectangle {
id: mainrect
implicitWidth: control.width
implicitHeight: control.height
visible: !control.flat || control.down || control.checked || control.highlighted
x: control.left
y: control.top
color: Color.blend(control.checked || control.highlighted ? control.palette.dark : control.palette.button,
control.palette.mid, control.down ? 0.5 : 0.0)
border.color: control.palette.highlight
border.width: control.visualFocus ? 2 : 0
Rectangle
{
implicitHeight: control.height
implicitWidth: 6 implicitWidth: 6
x: mainrect.left visible: control.checked || control.hovered
y: mainrect.top
color: "yellow"
} }
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: mouse => mouse.accepted = false
} }
onClicked: {
if(!target) {
return
}
contentStack.replace(target)
}
} }

View File

@@ -1,30 +1,69 @@
import QtQuick import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.impl as I
import QtQuick.Templates as T import QtQuick.Templates as T
T.Button { T.Button {
id: control id: control
implicitHeight: implicitContentHeight /**
implicitWidth: contentItem.width * Set true when the button is supposed to be displayed in e.g. a TextField.
* You want to do this when this button is directly related to the TextField
* and the primary and only action for the TextField.
* Usually, you'd only want to display an icon in this button.
* If true, automatically sets height, width and position.
*
* ```qml
* TextField {
* placeholderText: "Search..."
* Button {
* icon.source: "qrc:/images/MagnifyingGlass.svg"
* isFieldButton: true
* }
* }
* ```
*/
property bool isFieldButton: false
height: isFieldButton ? parent.height : null
icon.color: Colors.primaryContrast
icon.height: 21
icon.width: 21
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
/**
* Icon is slightly larger than Text, so we need to reduce the padding a
* tiny bit to make sure all Buttons are still the same height.
*/
padding: Dimensions.m - (icon.source.toString() === "" ? 0 : 1)
x: isFieldButton ? parent.x + parent.width - width : null
background: Rectangle { background: Rectangle {
color: Colors.primary anchors.fill: parent
border.color: Colors.interactive
border.width: isFieldButton ? 1 : 0
bottomLeftRadius: topLeftRadius
color: !control.enabled ? Colors.disabled : !control.hovered ? Colors.primary : Colors.primaryLighter
radius: Dimensions.radius radius: Dimensions.radius
topLeftRadius: isFieldButton ? 0 : radius
} }
contentItem: Text { contentItem: I.IconLabel {
color: Colors.foreground color: !control.enabled ? Colors.disabledForeground : Colors.primaryContrast
display: control.display
font: control.font font: control.font
padding: Dimensions.s + 2 icon: control.icon
mirrored: control.mirrored
spacing: Dimensions.s
text: control.text text: control.text
// Make sure the button is at least wide enough to be comfortably clickable
width: Math.max(implicitWidth, 120)
horizontalAlignment: Text.AlignHCenter
} }
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: (mouse) => mouse.accepted = false
onPressed: mouse => mouse.accepted = false
} }
} }

View File

@@ -6,14 +6,21 @@ QtObject {
readonly property int dark: 0 readonly property int dark: 0
readonly property int light: 1 readonly property int light: 1
property int theme: dark property int theme: Application.styleHints.colorScheme === Qt.ColorScheme.Light ? light : dark
readonly property color primary: "#b81a34" readonly property color primary: "#b81a34"
readonly property color primaryContrast: "#fdfdfd"
readonly property color primaryLighter: Qt.lighter(primary, 1.5)
readonly property color primaryShade: theme === dark ? primaryLighter : Qt.darker(primary, 1.5)
readonly property color primaryHighlight: theme === dark ? Qt.darker(primary, 2- Colors.highlightOpacity) : Qt.lighter(primary, 2- Colors.highlightOpacity)
readonly property color foreground: theme === dark ? "#fdfdfd" : "#110b0c" readonly property color foreground: theme === dark ? "#fdfdfd" : "#110b0c"
readonly property color background: theme === dark ? "#303136" : "#eff1f5" readonly property color background: theme === dark ? "#303136" : "#eff1f5"
readonly property color mantle: theme === dark ? "#1e1f22" : "#e7e9ef" readonly property color mantle: theme === dark ? "#1E1E23" : "#e7e9ef"
readonly property color interactive: theme === dark ? "#878b97" : "#d9d9da" readonly property color interactive: theme === dark ? "#878b97" : "#d9d9da"
readonly property color error: theme === dark ? "#ff2264" : "#ff004b" readonly property color error: theme === dark ? "#ff2264" : "#ff004b"
readonly property color disabled: theme === dark ? Qt.darker(interactive, 1.9) : Qt.darker(interactive, 1.3)
readonly property color disabledForeground: theme === dark ? Qt.darker(foreground, 1.4) : Qt.lighter(foreground, 1.9)
readonly property color transparent: "transparent"
readonly property double highlightOpacity: .3 readonly property double highlightOpacity: .3
} }

View File

@@ -33,36 +33,16 @@ T.ComboBox {
border.color: Colors.interactive border.color: Colors.interactive
border.width: 1 border.width: 1
color: Colors.mantle color: Colors.mantle
// height: parent.height
radius: Dimensions.radius radius: Dimensions.radius
width: parent.width width: parent.width
} }
// delegate: MenuItem {
// id: menuItem
// required property int index
// required property var model
// highlighted: control.highlightedIndex === index
// hoverEnabled: control.hoverEnabled
// text: model[control.textRole]
// width: control.width
// background: Rectangle {
// color: menuItem.down || menuItem.highlighted ? Colors.primary : "transparent"
// height: menuItem.height
// width: menuItem.width
// }
// }
delegate: ItemDelegate { delegate: ItemDelegate {
required property var model required property var model
required property int index required property int index
width: ListView.view.width width: ListView.view.width
text: model[control.textRole] text: model[control.textRole]
// palette.text: control.palette.text
// palette.highlightedText: control.palette.highlightedText
// font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
highlighted: control.highlightedIndex === index highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled hoverEnabled: control.hoverEnabled
} }
@@ -125,8 +105,4 @@ T.ComboBox {
} }
} }
} }
Component.onCompleted:
{
console.log(control.implicitContentHeight)
}
} }

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /([\+!#$%&\*\\/\=?\^_`\.{|}\~\-\_0-9A-Za-z]{1,185})@([0-9A-Za-z\.\-\_]{1,64})\.([a-zA-z]{2,5})/
}

View File

@@ -5,11 +5,16 @@ import QtQuick.Layouts
ColumnLayout ColumnLayout
{ {
required property string label required property string label
/**
* Adds an asterisk after the label, informing the user that this field
* is mandatory.
*/
property bool mandatory: false
spacing: Dimensions.s spacing: Dimensions.s
Label Label
{ {
text: label text: label + (mandatory ? "*" : "")
font: Typography.body font: Typography.body
} }
} }

5
TeroStyle/H1.qml Normal file
View File

@@ -0,0 +1,5 @@
import QtQuick
Text {
font: Typography.h1
}

5
TeroStyle/H2.qml Normal file
View File

@@ -0,0 +1,5 @@
import QtQuick
Text {
font: Typography.h2
}

5
TeroStyle/Label.qml Normal file
View File

@@ -0,0 +1,5 @@
import QtQuick.Controls
Label {
color: Colors.foreground
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /^\S+.*\S+$/
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /^$|([\+!#$%&\*\\/\=?\^_`\.{|}\~\-\_0-9A-Za-z]{1,185})@([0-9A-Za-z\.\-\_]{1,64})\.([a-zA-z]{2,5})/
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /^$|([+0-9])([0-9\s]{1,17})/
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /([+0-9])([0-9\s]{1,17})/
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /([^$][0-9]{1,4})/
}

64
TeroStyle/QuickFilter.qml Normal file
View File

@@ -0,0 +1,64 @@
import QtQuick
import QtCore
import QtQuick.Layouts
RowLayout {
id: root
required property ListModel model
signal selectedChanged(string name)
spacing: Dimensions.m
Repeater {
model: root.model
Item {
id: item
required property int index
required property var modelData
property bool hovered: false
property real padding: Dimensions.m
height: text.height + padding * 2
width: text.width + padding * 2
Rectangle {
anchors.fill: parent
border.color: modelData.selected ? Colors.transparent : Colors.foreground
border.width: 2
color: modelData.selected || mouseArea.containsMouse ? Colors.primary : Colors.transparent
radius: parent.height
}
Text {
id: text
color: Colors.foreground
font: Typography.body
text: modelData.text
x: parent.padding
y: parent.padding
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onPressed: {
if (item.modelData.selected)
return;
const model = root.model;
for (let i = 0; i < model.count; i++) {
model.setProperty(i, "selected", false);
}
model.setProperty(item.index, "selected", true);
selectedChanged(item.modelData.name)
}
}
}
}
}

26
TeroStyle/SearchBar.qml Normal file
View File

@@ -0,0 +1,26 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
TextField {
id: field
signal submitted(query: string)
Layout.preferredWidth: 300
placeholderText: qsTr("Suche")
Keys.onReturnPressed: {
field.submitted(field.text);
}
Button {
icon.source: "qrc:/images/MagnifyingGlass.svg"
isFieldButton: true
onClicked: {
field.submitted(field.text)
}
}
}

View File

@@ -4,6 +4,7 @@ import QtQuick.Templates as T
T.TextField T.TextField
{ {
id: control id: control
background: Rectangle background: Rectangle
{ {
id: background id: background
@@ -19,7 +20,28 @@ T.TextField
placeholderTextColor: Colors.interactive placeholderTextColor: Colors.interactive
font: Typography.body font: Typography.body
padding: Dimensions.m padding: Dimensions.m
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
/**
* Placeholder
*/
Text {
x: control.leftPadding
y: control.topPadding
width: control.width - (control.leftPadding + control.rightPadding)
height: control.height - (control.topPadding + control.bottomPadding)
font: control.font
text: control.placeholderText
color: control.placeholderTextColor
verticalAlignment: control.verticalAlignment
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
elide: Text.ElideRight
renderType: control.renderType
}
} }

View File

@@ -3,38 +3,54 @@ pragma Singleton
import QtCore import QtCore
import QtQuick import QtQuick
Item Item {
{ readonly property font body: ({
readonly property FontLoader robotoCondensed: FontLoader
{
source: "qrc:/fonts/RobotoCondensed.otf"
}
readonly property font body:
({
family: robotoCondensed.font, family: robotoCondensed.font,
pointSize: 16, pointSize: 16,
weight: Font.Medium, weight: Font.Medium,
letterSpacing: 0, letterSpacing: 0,
kerning: true, kerning: true
}) })
readonly property font h1: readonly property font small: ({
({ family: body.family,
pointSize: 14,
weight: Font.Medium,
letterSpacing: body.letterSpacing,
kerning: body.kerning
})
readonly property font smallBold: ({
family: small.family,
pointSize: small.pointSize,
weight: Font.Bold,
letterSpacing: small.letterSpacing,
kerning: small.kerning
})
readonly property font smaller: ({
family: body.family,
pointSize: 11,
weight: Font.DemiBold,
letterSpacing: body.letterSpacing,
kerning: body.kerning
})
readonly property font h1: ({
family: body.family, family: body.family,
pointSize: 38, pointSize: 38,
weight: body.weight, weight: body.weight,
letterSpacing: body.letterSpacing, letterSpacing: body.letterSpacing,
kerning: body.kerning, kerning: body.kerning
}) })
readonly property font h2: ({
readonly property font dash:
({
family: body.family, family: body.family,
pointSize: 10, pointSize: 28,
weight: body.weight, weight: body.weight,
letterSpacing: body.letterSpacing, letterSpacing: body.letterSpacing,
kerning: body.kerning, kerning: body.kerning,
}) })
readonly property FontLoader robotoCondensed: FontLoader {
source: "qrc:/fonts/RobotoCondensed.otf"
}
} }

View File

@@ -2,8 +2,19 @@ module TeroStyle
singleton Colors Colors.qml singleton Colors Colors.qml
singleton Dimensions Dimensions.qml singleton Dimensions Dimensions.qml
singleton Typography Typography.qml singleton Typography Typography.qml
BarButton BarButton.qml
Button Button.qml Button Button.qml
ComboBox ComboBox.qml ComboBox ComboBox.qml
EmailAddressValidator EmailAddressValidator.qml
Field Field.qml Field Field.qml
H1 H1.qml
H2 H2.qml
Label Label.qml
NotEmptyValidator NotEmptyValidator.qml
OptionalEmailAddressValidator OptionalEmailAddressValidator.qml
PhoneNumberValidator PhoneNumberValidator.qml
OptionalPhoneNumberValidator OptionalPhoneNumberValidator.qml
PostcodeValidator PostcodeValidator.qml
QuickFilter QuickFilter.qml
SearchBar SearchBar.qml
TextField TextField.qml TextField TextField.qml
BarButton BarButton.qml

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +0,0 @@
# pyqcrm
CRM - Tero
Python + QML

BIN
doc/design/TERO CRM.penpot Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 142 KiB

14
docker-compose.yml Normal file
View File

@@ -0,0 +1,14 @@
services:
mariadb:
image: mariadb:latest
volumes:
- mariadb:/var/lib/mysql
ports:
- 127.0.0.1:8000:3306
environment:
MARIADB_ROOT_PASSWORD: pyqcrm
MARIADB_USER: pyqcrm
MARIADB_PASSWORD: pyqcrm
MARIADB_DATABASE: pyqcrm
volumes:
mariadb:

View File

@@ -0,0 +1,3 @@
<svg fill="none" stroke-width="2" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="m11.25 9-3 3m0 0 3 3m-3-3h7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 288 B

View File

@@ -0,0 +1,4 @@
<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon" data-darkreader-inline-fill="">
<path clip-rule="evenodd" fill-rule="evenodd" d="M17 4.25A2.25 2.25 0 0 0 14.75 2h-5.5A2.25 2.25 0 0 0 7 4.25v2a.75.75 0 0 0 1.5 0v-2a.75.75 0 0 1 .75-.75h5.5a.75.75 0 0 1 .75.75v11.5a.75.75 0 0 1-.75.75h-5.5a.75.75 0 0 1-.75-.75v-2a.75.75 0 0 0-1.5 0v2A2.25 2.25 0 0 0 9.25 18h5.5A2.25 2.25 0 0 0 17 15.75V4.25Z"></path>
<path clip-rule="evenodd" fill-rule="evenodd" d="M1 10a.75.75 0 0 1 .75-.75h9.546l-1.048-.943a.75.75 0 1 1 1.004-1.114l2.5 2.25a.75.75 0 0 1 0 1.114l-2.5 2.25a.75.75 0 1 1-1.004-1.114l1.048-.943H1.75A.75.75 0 0 1 1 10Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 708 B

3
images/Bars3.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75ZM2 10a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 10Zm0 5.25a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 521 B

3
images/CheckCircle.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -1,3 +1,3 @@
<svg fill="none" stroke-width="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon" data-darkreader-inline-stroke=""> <svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path> <path clip-rule="evenodd" fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 318 B

3
images/Funnel.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M2.628 1.601C5.028 1.206 7.49 1 10 1s4.973.206 7.372.601a.75.75 0 0 1 .628.74v2.288a2.25 2.25 0 0 1-.659 1.59l-4.682 4.683a2.25 2.25 0 0 0-.659 1.59v3.037c0 .684-.31 1.33-.844 1.757l-1.937 1.55A.75.75 0 0 1 8 18.25v-5.757a2.25 2.25 0 0 0-.659-1.591L2.659 6.22A2.25 2.25 0 0 1 2 4.629V2.34a.75.75 0 0 1 .628-.74Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 495 B

View File

Before

Width:  |  Height:  |  Size: 539 B

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,4 @@
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M5.478 5.559A1.5 1.5 0 0 1 6.912 4.5H9A.75.75 0 0 0 9 3H6.912a3 3 0 0 0-2.868 2.118l-2.411 7.838a3 3 0 0 0-.133.882V18a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3v-4.162c0-.299-.045-.596-.133-.882l-2.412-7.838A3 3 0 0 0 17.088 3H15a.75.75 0 0 0 0 1.5h2.088a1.5 1.5 0 0 1 1.434 1.059l2.213 7.191H17.89a3 3 0 0 0-2.684 1.658l-.256.513a1.5 1.5 0 0 1-1.342.829h-3.218a1.5 1.5 0 0 1-1.342-.83l-.256-.512a3 3 0 0 0-2.684-1.658H3.265l2.213-7.191Z"></path>
<path clip-rule="evenodd" fill-rule="evenodd" d="M12 2.25a.75.75 0 0 1 .75.75v6.44l1.72-1.72a.75.75 0 1 1 1.06 1.06l-3 3a.75.75 0 0 1-1.06 0l-3-3a.75.75 0 0 1 1.06-1.06l1.72 1.72V3a.75.75 0 0 1 .75-.75Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 824 B

View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11ZM2 9a7 7 0 1 1 12.452 4.391l3.328 3.329a.75.75 0 1 1-1.06 1.06l-3.329-3.328A7 7 0 0 1 2 9Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 319 B

View File

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 501 B

3
images/Phone.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M1.5 4.5a3 3 0 0 1 3-3h1.372c.86 0 1.61.586 1.819 1.42l1.105 4.423a1.875 1.875 0 0 1-.694 1.955l-1.293.97c-.135.101-.164.249-.126.352a11.285 11.285 0 0 0 6.697 6.697c.103.038.25.009.352-.126l.97-1.293a1.875 1.875 0 0 1 1.955-.694l4.423 1.105c.834.209 1.42.959 1.42 1.82V19.5a3 3 0 0 1-3 3h-2.25C8.552 22.5 1.5 15.448 1.5 6.75V4.5Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 514 B

3
images/PlusCircle.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm.75-11.25a.75.75 0 0 0-1.5 0v2.5h-2.5a.75.75 0 0 0 0 1.5h2.5v2.5a.75.75 0 0 0 1.5 0v-2.5h2.5a.75.75 0 0 0 0-1.5h-2.5v-2.5Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 344 B

6
images/README.md Normal file
View File

@@ -0,0 +1,6 @@
Icons are sourced from [heroicons.dev](https://heroicons.dev/?iconset=v2-20-solid&size=21) and licensed under the terms of the MIT license.
## Solid or Outline?
Get the 20px Solid icon for most cases, since solid icons are the easiest for users to scan.
You might need an Outline icon in some cases, e.g. because the icon renders so big that a solid icon looks "boring". Suffix outline icon files with `-Outline`) and change the `stroke-width` attribute in the .svg to 2.

View File

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View File

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 418 B

3
images/UserCircle.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-slot="icon">
<path clip-rule="evenodd" fill-rule="evenodd" d="M18.685 19.097A9.723 9.723 0 0 0 21.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 0 0 3.065 7.097A9.716 9.716 0 0 0 12 21.75a9.716 9.716 0 0 0 6.685-2.653Zm-12.54-1.285A7.486 7.486 0 0 1 12 15a7.486 7.486 0 0 1 5.855 2.812A8.224 8.224 0 0 1 12 20.25a8.224 8.224 0 0 1-5.855-2.438ZM15.75 9a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 539 B

View File

Before

Width:  |  Height:  |  Size: 733 B

After

Width:  |  Height:  |  Size: 733 B

View File

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 490 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm246-164q-59 0-99.5-40.5T340-580q0-59 40.5-99.5T480-720q59 0 99.5 40.5T620-580q0 59-40.5 99.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q53 0 100-15.5t86-44.5q-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160Zm0-360q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm0-60Zm0 360Z"/></svg>

Before

Width:  |  Height:  |  Size: 751 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>

Before

Width:  |  Height:  |  Size: 182 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M720-40v-120H600v-80h120v-120h80v120h120v80H800v120h-80ZM80-160v-240H40v-80l40-200h600l40 200v80h-40v120h-80v-120H440v240H80Zm80-80h200v-160H160v160Zm-38-240h516-516ZM80-720v-80h600v80H80Zm42 240h516l-24-120H146l-24 120Z"/></svg>

Before

Width:  |  Height:  |  Size: 345 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F6368"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h360v80H200v560h560v-360h80v360q0 33-23.5 56.5T760-120H200Zm120-160v-80h320v80H320Zm0-120v-80h320v80H320Zm0-120v-80h320v80H320Zm360-80v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Z"/></svg>

Before

Width:  |  Height:  |  Size: 362 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M720-400v-120H600v-80h120v-120h80v120h120v80H800v120h-80Zm-360-80q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm80-80h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0-80Zm0 400Z"/></svg>

Before

Width:  |  Height:  |  Size: 603 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-308 198-252H282l198 252Zm0 0Z"/></svg>

Before

Width:  |  Height:  |  Size: 290 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/></svg>

Before

Width:  |  Height:  |  Size: 193 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>

Before

Width:  |  Height:  |  Size: 376 B

19
lib/Config.py Normal file
View File

@@ -0,0 +1,19 @@
from typing import TypedDict
class EncryptionConfig(TypedDict):
ENCRYPTION_KEY_VALID: str
ENCRYPTION_KEY: str
class DatabaseConfig(TypedDict):
DB_HOST: str
DB_USER: str
DB_PASS: str
DB_NAME: str
DB_PORT: str
class Config(TypedDict):
database: DatabaseConfig
pyqcrm: EncryptionConfig

Some files were not shown because too many files have changed in this diff Show More