diff --git a/Cargo.lock b/Cargo.lock index 8ba6ad9..a81b261 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,18 +266,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -302,12 +290,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -326,6 +308,56 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -708,6 +740,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "comemo" version = "0.4.0" @@ -796,9 +834,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -904,7 +942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca" dependencies = [ "commoncrypto", - "hex", + "hex 0.3.2", "openssl", "winapi", ] @@ -945,6 +983,38 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" +[[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "deranged" version = "0.5.8" @@ -1050,6 +1120,37 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elm-gen-timeclonk" +version = "0.1.0" +dependencies = [ + "elm_rs", + "orgauth", + "protocol", +] + +[[package]] +name = "elm_rs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8fe591d505f32055ae43c610e0e5491d42d57755998e7420c7f06edb201373" +dependencies = [ + "elm_rs_derive", + "uuid", +] + +[[package]] +name = "elm_rs_derive" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab0285635ea7c491a7009ef8d85425f7b29b7b71f09da0444944ed912ad07b" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "email-encoding" version = "0.2.1" @@ -1107,6 +1208,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "env_filter" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.5.13" @@ -1120,6 +1231,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1138,9 +1262,9 @@ dependencies = [ [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" @@ -1174,12 +1298,6 @@ dependencies = [ "instant", ] -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - [[package]] name = "fdeflate" version = "0.3.7" @@ -1271,6 +1389,15 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.32" @@ -1406,16 +1533,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1433,11 +1550,11 @@ checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "hashlink" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.5", ] [[package]] @@ -1488,6 +1605,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkdf" version = "0.12.4" @@ -1506,17 +1629,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.12" @@ -1528,6 +1640,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "http-range" version = "0.1.5" @@ -1564,6 +1687,30 @@ dependencies = [ "typenum", ] +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hypher" version = "0.1.7" @@ -1930,12 +2077,49 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634" +dependencies = [ + "defmt", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -2009,13 +2193,10 @@ dependencies = [ "base64 0.21.7", "email-encoding", "email_address", - "fastrand 1.9.0", - "futures-util", - "hostname", + "fastrand", "httpdate", "idna 0.3.0", "mime", - "native-tls", "nom", "once_cell", "quoted_printable", @@ -2037,10 +2218,11 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ + "cc", "pkg-config", "vcpkg", ] @@ -2051,12 +2233,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - [[package]] name = "lipsum" version = "0.9.1" @@ -2114,12 +2290,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "memchr" version = "2.8.0" @@ -2176,23 +2346,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "7.1.3" @@ -2258,6 +2411,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -2289,12 +2448,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - [[package]] name = "openssl-sys" version = "0.9.115" @@ -2316,21 +2469,24 @@ dependencies = [ "barrel", "base64 0.13.1", "clap", - "crypto-hash", "either", - "env_logger", + "elm_rs", + "env_logger 0.11.11", "futures", "json", "lettre", "log", "rand 0.5.6", + "reqwest", "rusqlite", "serde", "serde_derive", "serde_json", + "sha256", "simple-error", - "time 0.2.27", + "time 0.3.47", "timer", + "tokio", "toml 0.5.11", "uuid", ] @@ -2498,6 +2654,15 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "postcard" version = "1.1.3" @@ -2544,6 +2709,28 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -2559,6 +2746,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "protocol" +version = "0.1.0" +dependencies = [ + "elm_rs", + "orgauth", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "psm" version = "0.1.31" @@ -2766,6 +2964,42 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "roxmltree" version = "0.19.0" @@ -2774,9 +3008,9 @@ checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" [[package]] name = "rusqlite" -version = "0.29.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ "bitflags 2.11.1", "fallible-iterator", @@ -2804,19 +3038,6 @@ dependencies = [ "semver 1.0.28", ] -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - [[package]] name = "rustversion" version = "1.0.22" @@ -2854,44 +3075,12 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.1", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "0.9.0" @@ -3027,6 +3216,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha256" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f880fc8562bdeb709793f00eb42a2ad0e672c4f883bbe59122b926eca935c8f6" +dependencies = [ + "async-trait", + "bytes", + "hex 0.4.3", + "sha2", + "tokio", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3285,6 +3487,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.13.2" @@ -3318,16 +3526,24 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.27.0" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "fastrand 2.4.1", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -3469,11 +3685,13 @@ dependencies = [ "clap", "crypto-hash", "either", - "env_logger", + "elm_rs", + "env_logger 0.5.13", "futures", "json", "log", "orgauth", + "protocol", "rand 0.5.6", "rusqlite", "serde", @@ -3557,9 +3775,21 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2 0.6.3", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -3632,6 +3862,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" @@ -3664,6 +3900,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "ttf-parser" version = "0.20.0" @@ -4003,14 +4245,23 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" -version = "0.8.2" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.2.17", - "serde", + "getrandom 0.4.2", + "js-sys", + "rand 0.10.1", + "serde_core", + "wasm-bindgen", ] [[package]] @@ -4047,6 +4298,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -4084,6 +4344,16 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.120" @@ -4190,6 +4460,16 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "web-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" version = "0.1.12" @@ -4286,13 +4566,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -4304,34 +4593,67 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4344,24 +4666,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -4377,6 +4723,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index dd5da28..94599b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ [workspace] members = [ "server", - "orgauth/rustlib", + "elm-gen", + "orgauth/rustlib", "protocol", ] resolver = "2" diff --git a/elm-gen/Cargo.toml b/elm-gen/Cargo.toml new file mode 100644 index 0000000..de15106 --- /dev/null +++ b/elm-gen/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "elm-gen-timeclonk" +version = "0.1.0" +edition = "2021" + +[dependencies] +elm_rs = { version = "0.2.3", features = ["uuid"] } +orgauth = { path = "../orgauth/rustlib" } +protocol = { path = "../protocol" } diff --git a/elm-gen/src/main.rs b/elm-gen/src/main.rs new file mode 100644 index 0000000..5197f4d --- /dev/null +++ b/elm-gen/src/main.rs @@ -0,0 +1,92 @@ +use std::path::Path; + +use orgauth::util; +use protocol::{ + data::{ + Allocation, ExtraField, InvoiceItem, ListProject, PayEntry, PayType, PrintInvoice, Project, + ProjectEdit, ProjectId, ProjectMember, ProjectTime, Role, SaveAllocation, SavePayEntry, + SaveProject, SaveProjectEdit, SaveProjectInvoice, SaveProjectMember, SaveProjectTime, + SaveTimeEntry, SavedProjectEdit, TimeEntry, User, + }, + messages::{PublicMessageX, PublicResponseX, TcMessageX, TcResponseX, TimeClonkError}, +}; + +fn main() -> Result<(), Box> { + let ed = Path::new("../elm/src"); + + // TcMessage.elm + { + let mut target = vec![]; + // elm_rs provides a macro for conveniently creating an Elm module with everything needed + elm_rs::export!( + "TcProtocol", + &mut target, + { // generates types and encoders for types implementing ElmEncoder + encoders: [ + Project, + ProjectTime, + PayEntry, + ProjectMember, + Allocation, + TimeEntry, + TcMessageX, + ProjectId, + PublicMessageX, + PrintInvoice, + InvoiceItem, + Role, + PayType, + ExtraField, + SaveProjectEdit,SaveProjectMember, SaveProject, + SavedProjectEdit, + SaveProjectInvoice, + SaveProjectTime, + SaveTimeEntry, + SaveAllocation, + SavePayEntry], + // generates types and decoders for types implementing ElmDecoder + decoders: [ + TcResponseX ,PublicResponseX, + ListProject, + TimeClonkError, + ProjectId, + Project, + ProjectEdit, + ProjectTime, + PayEntry, + ProjectMember, + Allocation, + SavedProjectEdit, + TimeEntry, + User, + Role, + ExtraField, + PayType], + // generates types and functions for forming queries for types implementing ElmQuery + queries: [], + // generates types and functions for forming queries for types implementing ElmQueryField + query_fields: [], + } + ) + .unwrap(); + + let output = String::from_utf8(target).unwrap(); + + // add line importing Orgauth.Userid + let uidout = output.replace( + "import Json.Encode", + r#"import Json.Encode +import Orgauth.Data exposing (UserId(..), userIdDecoder, userIdEncoder)"#, + ); + + let outf = ed + .join("TcProtocol.elm") + .to_str() + .expect("bad path") + .to_string(); + util::write_string(outf.as_str(), uidout.as_str())?; + println!("wrote file: {}", outf); + } + + Ok(()) +} diff --git a/elm/src/Data.elm b/elm/src/Data.elm deleted file mode 100644 index e514a13..0000000 --- a/elm/src/Data.elm +++ /dev/null @@ -1,856 +0,0 @@ -module Data exposing - ( Allocation - , AllocationId(..) - , ExtraField - , InvoiceItem - , ListProject - , LoginData - , PayEntry - , PayEntryId(..) - , PayType(..) - , PrintInvoice - , PrintInvoiceInternal - , Project - , ProjectEdit - , ProjectId(..) - , ProjectMember - , ProjectTime - , Role(..) - , SaveAllocation - , SavePayEntry - , SaveProject - , SaveProjectEdit - , SaveProjectInvoice - , SaveProjectMember - , SaveProjectTime - , SaveTimeEntry - , SavedProject - , SavedProjectEdit - , TimeEntry - , TimeEntryId(..) - , User - , UserInviteData - , UserInviteProject - , decodeAllocation - , decodeListProject - , decodePayEntry - , decodeProject - , decodeProjectEdit - , decodeProjectMember - , decodeProjectTime - , decodeRole - , decodeSavedProject - , decodeSavedProjectEdit - , decodeTimeEntry - , decodeUser - , encodePrintInvoice - , encodeRole - , encodeSaveAllocation - , encodeSavePayEntry - , encodeSaveProject - , encodeSaveProjectEdit - , encodeSaveProjectInvoice - , encodeSaveProjectMember - , encodeSaveProjectTime - , encodeSaveTimeEntry - , encodeUserInviteData - , encodeUserInviteProject - , getAllocationIdVal - , getPayEntryIdVal - , getProjectIdVal - , getTimeEntryIdVal - , ldToOdLd - , makeAllocationId - , makeInvoiceId - , makePayEntryId - , makeProjectId - , makeTimeEntryId - , odLdToLd - , piDate - , projectMemberToUser - , roleToString - , showRole - , stringToRole - , toPi - -- , toPrintInvoice - , toSaveProjectInvoice - ) - -import Json.Decode as JD -import Json.Encode as JE -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) -import Time -import UUID exposing (UUID) -import Url.Builder as UB -import Util exposing (andMap) - - -type alias LoginData = - { userid : UserId - , name : String - , email : String - , admin : Bool - , active : Bool - } - - -ldToOdLd : LoginData -> OD.LoginData -ldToOdLd ld = - { userid = ld.userid - , name = ld.name - , email = ld.email - , admin = ld.admin - , active = ld.active - , data = JE.null - } - - -odLdToLd : OD.LoginData -> LoginData -odLdToLd ld = - { userid = ld.userid - , name = ld.name - , email = ld.email - , admin = ld.admin - , active = ld.active - } - - - ----------------------------------------- --- Timeclonk specific types. ----------------------------------------- - - -type alias UserInviteProject = - { id : ProjectId, role : Role } - - -encodeUserInviteProject : UserInviteProject -> JE.Value -encodeUserInviteProject p = - JE.object - [ ( "id", JE.int <| getProjectIdVal p.id ) - , ( "role", encodeRole p.role ) - ] - - -type alias UserInviteData = - { projects : List UserInviteProject } - - -encodeUserInviteData : UserInviteData -> JE.Value -encodeUserInviteData d = - JE.object - [ ( "projects", JE.list encodeUserInviteProject d.projects ) - ] - - -type alias ListProject = - { id : ProjectId - , name : String - , role : Role - } - - -type alias Project = - { id : ProjectId - , name : String - , description : String - , dueDays : Maybe Int - , extraFields : List ExtraField - , invoiceIdTemplate : String - , invoiceSeq : Int - , payer : String - , payee : String - , genericTask : String - , public : Bool - , rate : Maybe Float - , currency : Maybe String - , createdate : Int - , changeddate : Int - } - - -type alias SaveProjectInvoice = - { id : ProjectId - , extraFields : List ExtraField - , invoiceSeq : Int - } - - -type alias SaveProject = - { id : Maybe ProjectId - , name : String - , description : String - , dueDays : Maybe Int - , extraFields : List ExtraField - , invoiceIdTemplate : String - , invoiceSeq : Int - , payer : String - , payee : String - , genericTask : String - , public : Bool - , rate : Maybe Float - , currency : Maybe String - } - - -type alias SavedProject = - { id : ProjectId - , changeddate : Int - } - - -type Role - = Member - | Admin - | Observer - - -type alias ProjectMember = - { id : UserId - , name : String - , role : Role - } - - -showRole : Role -> String -showRole r = - case r of - Member -> - "member" - - Admin -> - "admin" - - Observer -> - "observer" - - -projectMemberToUser : ProjectMember -> User -projectMemberToUser pm = - { id = pm.id - , name = pm.name - } - - -type alias User = - { id : UserId - , name : String - } - - -type alias ProjectEdit = - { project : Project - , members : List ProjectMember - } - - -type alias SaveProjectMember = - { id : UserId - , role : Role - , delete : Bool - } - - -type alias SaveProjectEdit = - { project : SaveProject - , members : List SaveProjectMember - } - - -type alias SavedProjectEdit = - { project : Project - , members : List ProjectMember - } - - -type alias TimeEntry = - { id : TimeEntryId - , project : ProjectId - , user : UserId - , description : String - , startdate : Int - , enddate : Int - , ignore : Bool - , createdate : Int - , changeddate : Int - , creator : UserId - } - - -type alias SaveTimeEntry = - { id : Maybe TimeEntryId - , project : ProjectId - , user : UserId - , description : String - , startdate : Int - , enddate : Int - , ignore : Bool - } - - -type PayType - = Invoiced - | Paid - - -type alias PayEntry = - { id : PayEntryId - , project : ProjectId - , user : UserId - , duration : Int - , paytype : PayType - , paymentdate : Int - , description : String - , createdate : Int - , changeddate : Int - , creator : UserId - } - - -type alias SavePayEntry = - { id : Maybe PayEntryId - , project : ProjectId - , user : UserId - , duration : Int - , paytype : PayType - , paymentdate : Int - , description : String - } - - -type alias Allocation = - { id : AllocationId - , project : ProjectId - , duration : Int - , allocationdate : Int - , description : String - , createdate : Int - , changeddate : Int - , creator : UserId - } - - -type alias SaveAllocation = - { id : Maybe AllocationId - , project : ProjectId - , duration : Int - , allocationdate : Int - , description : String - } - - -type alias SaveProjectTime = - { project : ProjectId - , savetimeentries : List SaveTimeEntry - , deletetimeentries : List TimeEntryId - , savepayentries : List SavePayEntry - , deletepayentries : List PayEntryId - , saveallocations : List SaveAllocation - , deleteallocations : List AllocationId - } - - -type alias ProjectTime = - { project : Project - , members : List ProjectMember - , timeentries : List TimeEntry - , payentries : List PayEntry - , allocations : List Allocation - } - - -type alias InvoiceItem = - { description : String - , duration : Float - , rate : Float - } - - -type alias ExtraField = - { n : String - , v : String - } - - -type alias PrintInvoiceInternal = - { projectid : ProjectId - , seq : Int - , duedays : Maybe Int - , idtemplate : String - , payer : String - , payee : String - , items : List InvoiceItem - , extraFields : List ExtraField - , currency : Maybe String - } - - -makeInvoiceId : String -> String -> Int -> String -makeInvoiceId template date seq = - template - |> String.replace "" (String.fromInt seq) - |> String.replace "" date - - -piDate : Time.Posix -> Time.Zone -> String -piDate time zone = - (String.fromInt <| Time.toYear zone time) - ++ "-" - ++ (String.fromInt <| Util.monthInt <| Time.toMonth zone time) - ++ "-" - ++ (String.fromInt <| Time.toDay zone time) - - -toPi : - PrintInvoiceInternal - -> String - -> String - -> PrintInvoice -toPi pii date duedate = - { id = makeInvoiceId pii.idtemplate date pii.seq - , payer = pii.payer - , payee = pii.payee - , items = pii.items - , date = date - , dueDate = - if duedate == "" then - Nothing - - else - Just duedate - , extraFields = pii.extraFields - , currency = Maybe.withDefault "$" pii.currency - } - - -type alias PrintInvoice = - { id : String - , payer : String - , payee : String - , items : List InvoiceItem - , date : String - , dueDate : Maybe String - , extraFields : List ExtraField - , currency : String - } - - -toSaveProjectInvoice : PrintInvoiceInternal -> SaveProjectInvoice -toSaveProjectInvoice pi = - { id = pi.projectid - , extraFields = pi.extraFields - , invoiceSeq = pi.seq - } - - -encodeInvoiceItem : InvoiceItem -> JE.Value -encodeInvoiceItem ii = - JE.object - [ ( "description", JE.string ii.description ) - , ( "duration", JE.float ii.duration ) - , ( "rate", JE.float ii.rate ) - ] - - -encodePrintInvoice : PrintInvoice -> JE.Value -encodePrintInvoice pi = - JE.object <| - List.filterMap identity - [ Just ( "id", JE.string pi.id ) - , Just ( "payer", JE.string pi.payer ) - , Just ( "payee", JE.string pi.payee ) - , Just ( "date", JE.string pi.date ) - , pi.dueDate |> Maybe.map (\dd -> ( "due_date", JE.string dd )) - , Just ( "items", JE.list encodeInvoiceItem pi.items ) - , Just ( "extra_fields", JE.list encodeExtraField pi.extraFields ) - , Just ( "currency", JE.string pi.currency ) - ] - - - -------------------------------------------- --- Id types. They're all ints underneath. -------------------------------------------- - - -type ProjectId - = ProjectId Int - - -makeProjectId : Int -> ProjectId -makeProjectId i = - ProjectId i - - -getProjectIdVal : ProjectId -> Int -getProjectIdVal uid = - case uid of - ProjectId i -> - i - - -type PayEntryId - = PayEntryId Int - - -makePayEntryId : Int -> PayEntryId -makePayEntryId i = - PayEntryId i - - -getPayEntryIdVal : PayEntryId -> Int -getPayEntryIdVal uid = - case uid of - PayEntryId i -> - i - - -type TimeEntryId - = TimeEntryId Int - - -makeTimeEntryId : Int -> TimeEntryId -makeTimeEntryId i = - TimeEntryId i - - -getTimeEntryIdVal : TimeEntryId -> Int -getTimeEntryIdVal uid = - case uid of - TimeEntryId i -> - i - - -type AllocationId - = AllocationId Int - - -makeAllocationId : Int -> AllocationId -makeAllocationId i = - AllocationId i - - -getAllocationIdVal : AllocationId -> Int -getAllocationIdVal uid = - case uid of - AllocationId i -> - i - - -decodePayType : JD.Decoder PayType -decodePayType = - JD.string - |> JD.andThen - (\i -> - case i of - "Invoiced" -> - JD.succeed Invoiced - - "Paid" -> - JD.succeed Paid - - x -> - JD.fail ("invalid paytype: " ++ x) - ) - - -encodePayType : PayType -> JE.Value -encodePayType pt = - case pt of - Invoiced -> - JE.string "Invoiced" - - Paid -> - JE.string "Paid" - - - ----------------------------------------- --- Json encoders/decoders ----------------------------------------- - - -decodeListProject : JD.Decoder ListProject -decodeListProject = - JD.succeed ListProject - |> andMap (JD.field "id" JD.int |> JD.map makeProjectId) - |> andMap (JD.field "name" JD.string) - |> andMap (JD.field "role" decodeRole) - - -encodeSaveProjectInvoice : SaveProjectInvoice -> JE.Value -encodeSaveProjectInvoice sp = - JE.object <| - [ ( "id", JE.int (getProjectIdVal sp.id) ) - , ( "extra_fields", JE.list encodeExtraField sp.extraFields ) - , ( "invoice_seq", JE.int sp.invoiceSeq ) - ] - - -encodeSaveProject : SaveProject -> JE.Value -encodeSaveProject sp = - JE.object <| - List.filterMap identity - [ Just ( "name", JE.string sp.name ) - , Just ( "description", JE.string sp.description ) - , sp.dueDays |> Maybe.map (\dd -> ( "due_days", JE.int dd )) - , Just ( "extra_fields", JE.list encodeExtraField sp.extraFields ) - , Just ( "invoice_id_template", JE.string sp.invoiceIdTemplate ) - , Just ( "invoice_seq", JE.int sp.invoiceSeq ) - , Just ( "payer", JE.string sp.payer ) - , Just ( "payee", JE.string sp.payee ) - , Just ( "generic_task", JE.string sp.genericTask ) - , Just ( "public", JE.bool sp.public ) - , sp.rate |> Maybe.map (\rate -> ( "rate", JE.float rate )) - , sp.currency |> Maybe.map (\currency -> ( "currency", JE.string currency )) - , sp.id |> Maybe.map (\id -> ( "id", JE.int (getProjectIdVal id) )) - ] - - -encodeExtraField : ExtraField -> JE.Value -encodeExtraField ef = - JE.object - [ ( "n", JE.string ef.n ) - , ( "v", JE.string ef.v ) - ] - - -decodeExtraField : JD.Decoder ExtraField -decodeExtraField = - JD.succeed ExtraField - |> andMap (JD.field "n" JD.string) - |> andMap (JD.field "v" JD.string) - - -decodeProject : JD.Decoder Project -decodeProject = - JD.succeed Project - |> andMap (JD.field "id" JD.int |> JD.map makeProjectId) - |> andMap (JD.field "name" JD.string) - |> andMap (JD.field "description" JD.string) - |> andMap (JD.field "due_days" (JD.maybe JD.int)) - |> andMap (JD.field "extra_fields" <| JD.list decodeExtraField) - |> andMap (JD.field "invoice_id_template" JD.string) - |> andMap (JD.field "invoice_seq" JD.int) - |> andMap (JD.field "payer" JD.string) - |> andMap (JD.field "payee" JD.string) - |> andMap (JD.field "generic_task" JD.string) - |> andMap (JD.field "public" JD.bool) - |> andMap (JD.field "rate" <| JD.maybe JD.float) - |> andMap (JD.field "currency" <| JD.maybe JD.string) - |> andMap (JD.field "createdate" JD.int) - |> andMap (JD.field "changeddate" JD.int) - - -decodeSavedProject : JD.Decoder SavedProject -decodeSavedProject = - JD.succeed SavedProject - |> andMap (JD.field "id" JD.int |> JD.map makeProjectId) - |> andMap (JD.field "changeddate" JD.int) - - -stringToRole : String -> Result String Role -stringToRole s = - case s of - "Member" -> - Ok Member - - "Admin" -> - Ok Admin - - "Observer" -> - Ok Observer - - _ -> - Err ("invalid role string: " ++ s) - - -roleToString : Role -> String -roleToString r = - case r of - Member -> - "Member" - - Admin -> - "Admin" - - Observer -> - "Observer" - - -decodeRole : JD.Decoder Role -decodeRole = - JD.string - |> JD.andThen - (\s -> - case stringToRole s of - Ok r -> - JD.succeed r - - Err e -> - JD.fail e - ) - - -encodeRole : Role -> JE.Value -encodeRole r = - JE.string (roleToString r) - - -decodeProjectMember : JD.Decoder ProjectMember -decodeProjectMember = - JD.succeed ProjectMember - |> andMap (JD.field "id" JD.int |> JD.map makeUserId) - |> andMap (JD.field "name" JD.string) - |> andMap (JD.field "role" decodeRole) - - -decodeUser : JD.Decoder User -decodeUser = - JD.succeed User - |> andMap (JD.field "id" JD.int |> JD.map makeUserId) - |> andMap (JD.field "name" JD.string) - - -decodeProjectEdit : JD.Decoder ProjectEdit -decodeProjectEdit = - JD.succeed ProjectEdit - |> andMap (JD.field "project" decodeProject) - |> andMap (JD.field "members" (JD.list decodeProjectMember)) - - -encodeSaveProjectMember : SaveProjectMember -> JE.Value -encodeSaveProjectMember m = - JE.object - [ ( "id", JE.int (getUserIdVal m.id) ) - , ( "role", encodeRole m.role ) - , ( "delete", JE.bool m.delete ) - ] - - -encodeSaveProjectEdit : SaveProjectEdit -> JE.Value -encodeSaveProjectEdit p = - JE.object - [ ( "project", encodeSaveProject p.project ) - , ( "members", JE.list encodeSaveProjectMember p.members ) - ] - - -decodeSavedProjectEdit : JD.Decoder SavedProjectEdit -decodeSavedProjectEdit = - JD.succeed SavedProjectEdit - |> andMap (JD.field "project" decodeProject) - |> andMap (JD.field "members" <| JD.list decodeProjectMember) - - -encodeSaveProjectTime : SaveProjectTime -> JE.Value -encodeSaveProjectTime t = - JE.object - [ ( "project", JE.int (getProjectIdVal t.project) ) - , ( "savetimeentries", JE.list encodeSaveTimeEntry t.savetimeentries ) - , ( "deletetimeentries", JE.list (getTimeEntryIdVal >> JE.int) t.deletetimeentries ) - , ( "savepayentries", JE.list encodeSavePayEntry t.savepayentries ) - , ( "deletepayentries", JE.list (getPayEntryIdVal >> JE.int) t.deletepayentries ) - , ( "saveallocations", JE.list encodeSaveAllocation t.saveallocations ) - , ( "deleteallocations", JE.list (getAllocationIdVal >> JE.int) t.deleteallocations ) - ] - - -decodeTimeEntry : JD.Decoder TimeEntry -decodeTimeEntry = - JD.succeed TimeEntry - |> andMap (JD.field "id" JD.int |> JD.map makeTimeEntryId) - |> andMap (JD.field "project" JD.int |> JD.map makeProjectId) - |> andMap (JD.field "user" JD.int |> JD.map makeUserId) - |> andMap (JD.field "description" JD.string) - |> andMap (JD.field "startdate" JD.int) - |> andMap (JD.field "enddate" JD.int) - |> andMap (JD.field "ignore" JD.bool) - |> andMap (JD.field "createdate" JD.int) - |> andMap (JD.field "changeddate" JD.int) - |> andMap (JD.field "creator" JD.int |> JD.map makeUserId) - - -encodeSaveTimeEntry : SaveTimeEntry -> JE.Value -encodeSaveTimeEntry e = - JE.object <| - (e.id - |> Maybe.map (\id -> (::) ( "id", JE.int (getTimeEntryIdVal id) )) - |> Maybe.withDefault identity - ) - [ ( "project", JE.int (getProjectIdVal e.project) ) - , ( "user", JE.int (getUserIdVal e.user) ) - , ( "description", JE.string e.description ) - , ( "startdate", JE.int e.startdate ) - , ( "enddate", JE.int e.enddate ) - , ( "ignore", JE.bool e.ignore ) - ] - - -decodePayEntry : JD.Decoder PayEntry -decodePayEntry = - JD.succeed PayEntry - |> andMap (JD.field "id" JD.int |> JD.map makePayEntryId) - |> andMap (JD.field "project" JD.int |> JD.map makeProjectId) - |> andMap (JD.field "user" JD.int |> JD.map makeUserId) - |> andMap (JD.field "duration" JD.int) - |> andMap (JD.field "paytype" decodePayType) - |> andMap (JD.field "paymentdate" JD.int) - |> andMap (JD.field "description" JD.string) - |> andMap (JD.field "createdate" JD.int) - |> andMap (JD.field "changeddate" JD.int) - |> andMap (JD.field "creator" JD.int |> JD.map makeUserId) - - -encodeSavePayEntry : SavePayEntry -> JE.Value -encodeSavePayEntry e = - JE.object <| - (e.id - |> Maybe.map (\id -> (::) ( "id", JE.int (getPayEntryIdVal id) )) - |> Maybe.withDefault identity - ) - [ ( "project", JE.int (getProjectIdVal e.project) ) - , ( "user", JE.int (getUserIdVal e.user) ) - , ( "duration", JE.int e.duration ) - , ( "paytype", encodePayType e.paytype ) - , ( "paymentdate", JE.int e.paymentdate ) - , ( "description", JE.string e.description ) - ] - - -decodeAllocation : JD.Decoder Allocation -decodeAllocation = - JD.succeed Allocation - |> andMap (JD.field "id" JD.int |> JD.map makeAllocationId) - |> andMap (JD.field "project" JD.int |> JD.map makeProjectId) - |> andMap (JD.field "duration" JD.int) - |> andMap (JD.field "allocationdate" JD.int) - |> andMap (JD.field "description" JD.string) - |> andMap (JD.field "createdate" JD.int) - |> andMap (JD.field "changeddate" JD.int) - |> andMap (JD.field "creator" JD.int |> JD.map makeUserId) - - -encodeSaveAllocation : SaveAllocation -> JE.Value -encodeSaveAllocation e = - JE.object <| - (e.id - |> Maybe.map (\id -> (::) ( "id", JE.int (getAllocationIdVal id) )) - |> Maybe.withDefault identity - ) - [ ( "project", JE.int (getProjectIdVal e.project) ) - , ( "duration", JE.int e.duration ) - , ( "allocationdate", JE.int e.allocationdate ) - , ( "description", JE.string e.description ) - ] - - -decodeProjectTime : JD.Decoder ProjectTime -decodeProjectTime = - JD.succeed ProjectTime - |> andMap (JD.field "project" <| decodeProject) - |> andMap (JD.field "members" <| JD.list decodeProjectMember) - |> andMap (JD.field "timeentries" <| JD.list decodeTimeEntry) - |> andMap (JD.field "payentries" <| JD.list decodePayEntry) - |> andMap (JD.field "allocations" <| JD.list decodeAllocation) diff --git a/elm/src/DataUtil.elm b/elm/src/DataUtil.elm new file mode 100644 index 0000000..279dfd4 --- /dev/null +++ b/elm/src/DataUtil.elm @@ -0,0 +1,251 @@ +module DataUtil exposing (..) + +import Json.Encode as JE +import Orgauth.Data as OD exposing (UserId) +import TcProtocol exposing (ExtraField, InvoiceItem, PrintInvoice, ProjectId(..), ProjectMember, Role(..), SaveProjectInvoice, TimeClonkError(..), User, roleEncoder) +import Time +import Util + + +type alias LoginData = + { userid : UserId + , uuid : String + , name : String + , email : String + , admin : Bool + , active : Bool + } + + +ldToOdLd : LoginData -> OD.LoginData +ldToOdLd ld = + { userid = ld.userid + , uuid = ld.uuid + , name = ld.name + , email = ld.email + , admin = ld.admin + , active = ld.active + , remoteUrl = Nothing + , data = Nothing + } + + +odLdToLd : OD.LoginData -> LoginData +odLdToLd ld = + { userid = ld.userid + , uuid = ld.uuid + , name = ld.name + , email = ld.email + , admin = ld.admin + , active = ld.active + } + + +showTimeClonkError : TimeClonkError -> String +showTimeClonkError tce = + case tce of + TeNotLoggedIn -> + "not logged in" + + TeInvalidLogin -> + "invalid login" + + TeOther s -> + "server error: " ++ s + + +type alias UserInviteProject = + { id : ProjectId, role : Role } + + +encodeUserInviteProject : UserInviteProject -> JE.Value +encodeUserInviteProject p = + JE.object + [ ( "id", TcProtocol.projectIdEncoder p.id ) + , ( "role", roleEncoder p.role ) + ] + + +type alias UserInviteData = + { projects : List UserInviteProject } + + +encodeUserInviteData : UserInviteData -> JE.Value +encodeUserInviteData d = + JE.object + [ ( "projects", JE.list encodeUserInviteProject d.projects ) + ] + + +showRole : Role -> String +showRole r = + case r of + Member -> + "member" + + Admin -> + "admin" + + Observer -> + "observer" + + +projectMemberToUser : ProjectMember -> User +projectMemberToUser pm = + { id = pm.id + , name = pm.name + } + + +makeInvoiceId : String -> String -> Int -> String +makeInvoiceId template date seq = + template + |> String.replace "" (String.fromInt seq) + |> String.replace "" date + + +piDate : Time.Posix -> Time.Zone -> String +piDate time zone = + (String.fromInt <| Time.toYear zone time) + ++ "-" + ++ (String.fromInt <| Util.monthInt <| Time.toMonth zone time) + ++ "-" + ++ (String.fromInt <| Time.toDay zone time) + + +type alias PrintInvoiceInternal = + { projectid : ProjectId + , seq : Int + , duedays : Maybe Int + , idtemplate : String + , payer : String + , payee : String + , items : List InvoiceItem + , extraFields : List ExtraField + , currency : Maybe String + } + + +toPi : + PrintInvoiceInternal + -> String + -> String + -> PrintInvoice +toPi pii date duedate = + { id = makeInvoiceId pii.idtemplate date pii.seq + , payer = pii.payer + , payee = pii.payee + , items = pii.items + , date = date + , dueDate = + if duedate == "" then + Nothing + + else + Just duedate + , extraFields = pii.extraFields + , currency = Maybe.withDefault "$" pii.currency + } + + +toSaveProjectInvoice : PrintInvoiceInternal -> SaveProjectInvoice +toSaveProjectInvoice pi = + { id = pi.projectid + , extraFields = pi.extraFields + , invoiceSeq = pi.seq + } + + + +------------------------------------------- +-- Id types. They're all ints underneath. +------------------------------------------- + + +makeProjectId : Int -> ProjectId +makeProjectId i = + Pid i + + +getProjectIdVal : ProjectId -> Int +getProjectIdVal uid = + case uid of + Pid i -> + i + + +type PayEntryId + = PayEntryId Int + + +makePayEntryId : Int -> PayEntryId +makePayEntryId i = + PayEntryId i + + +getPayEntryIdVal : PayEntryId -> Int +getPayEntryIdVal uid = + case uid of + PayEntryId i -> + i + + +type TimeEntryId + = TimeEntryId Int + + +makeTimeEntryId : Int -> TimeEntryId +makeTimeEntryId i = + TimeEntryId i + + +getTimeEntryIdVal : TimeEntryId -> Int +getTimeEntryIdVal uid = + case uid of + TimeEntryId i -> + i + + +type AllocationId + = AllocationId Int + + +makeAllocationId : Int -> AllocationId +makeAllocationId i = + AllocationId i + + +getAllocationIdVal : AllocationId -> Int +getAllocationIdVal uid = + case uid of + AllocationId i -> + i + + +stringToRole : String -> Result String Role +stringToRole s = + case s of + "Member" -> + Ok Member + + "Admin" -> + Ok Admin + + "Observer" -> + Ok Observer + + _ -> + Err ("invalid role string: " ++ s) + + +roleToString : Role -> String +roleToString r = + case r of + Member -> + "Member" + + Admin -> + "Admin" + + Observer -> + "Observer" diff --git a/elm/src/InviteUser.elm b/elm/src/InviteUser.elm index a9eae8d..eac97ad 100644 --- a/elm/src/InviteUser.elm +++ b/elm/src/InviteUser.elm @@ -1,21 +1,17 @@ module InviteUser exposing (Command(..), Model, Msg(..), disabledLinkButtonStyle, emptyProjectDict, emptyProjectRoleDict, init, linkButtonStyle, update, view) import Common -import Data exposing (ProjectId, Role(..), getProjectIdVal, makeProjectId) +import DataUtil exposing (getProjectIdVal, makeProjectId) import Dict exposing (Dict(..)) import Element as E exposing (Element) -import Element.Background as EBk -import Element.Border as EBd -import Element.Events as EE import Element.Font as EF import Element.Input as EI -import Element.Region import Json.Encode as JE import Orgauth.Data as OD import TDict exposing (TDict(..)) import TangoColors import TcCommon as TC -import Time exposing (Zone) +import TcProtocol exposing (ListProject, ProjectId, Role(..)) import Util @@ -28,8 +24,8 @@ disabledLinkButtonStyle = type alias Model = - { ld : Data.LoginData - , projects : TDict ProjectId Int Data.ListProject + { ld : DataUtil.LoginData + , projects : TDict ProjectId Int ListProject , assignedProjects : TDict ProjectId Int Role , email : String } @@ -37,7 +33,7 @@ type alias Model = type Msg = EmailChanged String - | Add ProjectId Data.Role + | Add ProjectId Role | Remove ProjectId | OkClick | CancelClick @@ -55,12 +51,12 @@ emptyProjectRoleDict = TDict.empty getProjectIdVal makeProjectId -emptyProjectDict : TDict ProjectId Int Data.ListProject +emptyProjectDict : TDict ProjectId Int ListProject emptyProjectDict = TDict.empty getProjectIdVal makeProjectId -init : List Data.ListProject -> Data.LoginData -> Model +init : List ListProject -> DataUtil.LoginData -> Model init projects loginData = { ld = loginData , email = "" @@ -93,7 +89,7 @@ view stylePalette mbsize model = |> Maybe.map (\p -> p.name) |> Maybe.withDefault "" ) - , E.el [ E.alignRight ] <| E.text (Data.roleToString role) + , E.el [ E.alignRight ] <| E.text (DataUtil.roleToString role) , EI.button (E.alignRight :: Common.buttonStyle) { onPress = Just <| Remove pid , label = E.text "x" @@ -182,7 +178,7 @@ update msg model = else Nothing , data = - Data.encodeUserInviteData + DataUtil.encodeUserInviteData { projects = model.assignedProjects |> TDict.toList diff --git a/elm/src/Main.elm b/elm/src/Main.elm index 2429781..009ca58 100644 --- a/elm/src/Main.elm +++ b/elm/src/Main.elm @@ -1,64 +1,52 @@ port module Main exposing (main) -import Array import Browser import Browser.Events import Browser.Navigation -import Common exposing (buttonStyle) -import Data -import Dict exposing (Dict) +import Common +import DataUtil +import Dict import DisplayMessage import Element as E exposing (Element) -import Element.Background as EBk -import Element.Border as EBd import Element.Font as EF -import Element.Input as EI -import Element.Region import File as F import File.Download as FD import File.Select as FS import GenDialog as GD -import Html exposing (Attribute, Html) -import Html.Attributes -import Html.Events as HE +import Html exposing (Html) import Http import InviteUser import Json.Decode as JD import Json.Encode as JE import LocalStorage as LS -import Orgauth.AdminInterface as AI import Orgauth.ChangeEmail as CE import Orgauth.ChangePassword as CP -import Orgauth.Data as OD exposing (AdminSettings, UserId, getUserIdVal, makeUserId) +import Orgauth.Data as OD exposing (AdminSettings, UserId) import Orgauth.Invited as Invited import Orgauth.Login as Login import Orgauth.ResetPassword as ResetPassword import Orgauth.ShowUrl as ShowUrl import Orgauth.UserEdit as UserEdit -import Orgauth.UserInterface as UI import Orgauth.UserListing as UserListing import PrintInvoice as PI import ProjectEdit import ProjectListing import ProjectTime import ProjectView -import PublicInterface as PI import Random exposing (Seed, initialSeed) import Route exposing (Route(..), parseUrl, routeTitle, routeUrl) import SelectString as SS import ShowMessage -import TDict exposing (TDict) -import TangoColors as TC -import Task exposing (Task) +import TDict +import Task import TcCommon +import TcProtocol as TP import Time import TimeReporting as TR -import TimeclonkInterface as TI import Toop -import UUID exposing (UUID) +import UUID import Url exposing (Url) -import Url.Builder as UB -import Url.Parser as UP exposing (()) +import Url.Parser exposing (()) import UserSettings import UserTime import Util @@ -75,13 +63,13 @@ type Msg | UserTimeMsg UserTime.Msg | ShowUrlMsg ShowUrl.Msg | ShowMessageMsg ShowMessage.Msg - | UserReplyData (Result Http.Error UI.ServerResponse) - | AdminReplyData (Result Http.Error AI.ServerResponse) - | TimeclonkReplyData (Result Http.Error TI.ServerResponse) - | PublicReplyData (Result Http.Error PI.ServerResponse) - | ProjectTimeData String (Result Http.Error TI.ServerResponse) - | ProjectViewData String (Result Http.Error PI.ServerResponse) - | TProjectViewData String (Result Http.Error TI.ServerResponse) + | UserReplyData (Result Http.Error OD.UserResponse) + | AdminReplyData (Result Http.Error OD.AdminResponse) + | TimeclonkReplyData (Result Http.Error TP.TcResponseX) + | PublicReplyData (Result Http.Error TP.PublicResponseX) + | ProjectTimeData String (Result Http.Error TP.TcResponseX) + | ProjectViewData String (Result Http.Error TP.PublicResponseX) + | TProjectViewData String (Result Http.Error TP.TcResponseX) | PrintInvoiceReplyData (Result Http.Error (Cmd Msg)) | LoadUrl String | InternalUrl Url @@ -90,8 +78,8 @@ type Msg | WindowSize Util.Size | PrintInvoiceDialogMsg (GD.Msg PI.Msg) | DisplayMessageMsg (GD.Msg DisplayMessage.Msg) - | SelectUserDialogMsg (GD.Msg (SS.Msg Data.User)) - | SelectRoleDialogMsg (GD.Msg (SS.Msg ( UserId, Data.Role ))) + | SelectUserDialogMsg (GD.Msg (SS.Msg TP.User)) + | SelectRoleDialogMsg (GD.Msg (SS.Msg ( UserId, TP.Role ))) | ChangePasswordDialogMsg (GD.Msg CP.Msg) | ChangeEmailDialogMsg (GD.Msg CE.Msg) | ResetPasswordMsg ResetPassword.Msg @@ -105,34 +93,34 @@ type Msg | ProjectTimeMsg ProjectTime.Msg | FileLoaded (String -> Msg) F.File | TimeCmd (Time.Posix -> Cmd Msg) Time.Posix - | PrintInvoiceInit Data.PrintInvoiceInternal String String + | PrintInvoiceInit DataUtil.PrintInvoiceInternal String String | Noop type State = Login Login.Model | Invited Invited.Model - | InviteUser InviteUser.Model Data.LoginData - | UserSettings UserSettings.Model Data.LoginData State - | UserListing UserListing.Model Data.LoginData - | UserEdit UserEdit.Model Data.LoginData - | UserTime UserTime.Model Data.LoginData - | ShowUrl ShowUrl.Model Data.LoginData - | ShowMessage ShowMessage.Model Data.LoginData (Maybe State) + | InviteUser InviteUser.Model DataUtil.LoginData + | UserSettings UserSettings.Model DataUtil.LoginData State + | UserListing UserListing.Model DataUtil.LoginData + | UserEdit UserEdit.Model DataUtil.LoginData + | UserTime UserTime.Model DataUtil.LoginData + | ShowUrl ShowUrl.Model DataUtil.LoginData + | ShowMessage ShowMessage.Model DataUtil.LoginData (Maybe State) | PubShowMessage ShowMessage.Model (Maybe State) - | LoginShowMessage ShowMessage.Model Data.LoginData Url + | LoginShowMessage ShowMessage.Model DataUtil.LoginData Url | PrintInvoiceDialog PI.GDModel State - | SelectUserDialog (SS.GDModel Data.User) State - | SelectRoleDialog (SS.GDModel ( UserId, Data.Role )) State + | SelectUserDialog (SS.GDModel TP.User) State + | SelectRoleDialog (SS.GDModel ( UserId, TP.Role )) State | ChangePasswordDialog CP.GDModel State | ChangeEmailDialog CE.GDModel State | ResetPassword ResetPassword.Model | DisplayMessage DisplayMessage.GDModel State | Wait State (Model -> Msg -> ( Model, Cmd Msg )) - | ProjectListing ProjectListing.Model Data.LoginData - | ProjectEdit ProjectEdit.Model Data.LoginData - | ProjectView ProjectView.Model (Maybe Data.LoginData) - | ProjectTime ProjectTime.Model Data.LoginData + | ProjectListing ProjectListing.Model DataUtil.LoginData + | ProjectEdit ProjectEdit.Model DataUtil.LoginData + | ProjectView ProjectView.Model (Maybe DataUtil.LoginData) + | ProjectTime ProjectTime.Model DataUtil.LoginData type alias Flags = @@ -214,7 +202,7 @@ routeState model route = SettingsR -> case stateLogin model.state of Just login -> - ( UserSettings (UserSettings.init (Data.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state, Cmd.none ) + ( UserSettings (UserSettings.init (DataUtil.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state, Cmd.none ) Nothing -> ( (displayMessageDialog { model | state = initLoginState model } "can't view user settings; you're not logged in!").state, Cmd.none ) @@ -233,29 +221,29 @@ routeState model route = ProjectEditR id -> ( (displayMessageDialog model "loading project").state - , sendTIMsg model.location <| TI.GetProjectEdit id + , sendTcMsg model.location <| TP.TmGetProjectEdit (DataUtil.makeProjectId id) ) ProjectTimeR id mode -> ( (displayMessageDialog model "loading project").state - , sendTIMsgExp model.location (TI.GetProjectTime id) (ProjectTimeData mode) + , sendTcMsgExp model.location (TP.TmGetProjectTime (DataUtil.makeProjectId id)) (ProjectTimeData mode) ) ProjectViewR id mode -> case stateLogin model.state of Just login -> ( (displayMessageDialog model "loading project").state - , sendTIMsgExp model.location (TI.GetProjectTime id) (TProjectViewData mode) + , sendTcMsgExp model.location (TP.TmGetProjectTime (DataUtil.makeProjectId id)) (TProjectViewData mode) ) Nothing -> ( (displayMessageDialog model "loading project").state - , sendPIMsgExp model.location (PI.GetProjectTime id) (ProjectViewData mode) + , sendPIMsgExp model.location (TP.PmGetProjectTime (DataUtil.makeProjectId id)) (ProjectViewData mode) ) Invite token -> ( PubShowMessage { message = "retrieving invite" } Nothing - , sendUIMsg model.location (UI.ReadInvite token) + , sendUIMsg model.location (OD.UrqReadInvite token) ) @@ -272,7 +260,7 @@ stateRoute state = , save = True } - ResetPassword mod -> + ResetPassword _ -> { route = Top , save = False } @@ -281,7 +269,7 @@ stateRoute state = { route = case mod.id of Just pid -> - ProjectEditR (Data.getProjectIdVal pid) + ProjectEditR (DataUtil.getProjectIdVal pid) Nothing -> Top @@ -289,12 +277,12 @@ stateRoute state = } ProjectTime mod _ -> - { route = ProjectTimeR (Data.getProjectIdVal mod.project.id) (ProjectTime.showViewMode mod.viewmode) + { route = ProjectTimeR (DataUtil.getProjectIdVal mod.project.id) (ProjectTime.showViewMode mod.viewmode) , save = True } ProjectView mod _ -> - { route = ProjectViewR (Data.getProjectIdVal mod.project.id) (ProjectView.showViewMode mod.viewmode) + { route = ProjectViewR (DataUtil.getProjectIdVal mod.project.id) (ProjectView.showViewMode mod.viewmode) , save = True } @@ -339,105 +327,98 @@ showMessage msg = UserReplyData urd -> "UserReplyData: " - ++ (Result.map UI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map UI.showServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) AdminReplyData urd -> "AdminReplyData: " - ++ (Result.map AI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map AI.showServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) TimeclonkReplyData urd -> "TimeclonkReplyData: " - ++ (Result.map TI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map TP.TmshowServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) PublicReplyData urd -> "PublicReplyData: " - ++ (Result.map PI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map PI.showServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) ProjectTimeData mode urd -> "ProjectTimeData: " - ++ (Result.map TI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map TP.TmshowServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) ++ "\nmode: " ++ mode ProjectViewData mode urd -> "ProjectViewData: " - ++ (Result.map PI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map PI.showServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) ++ "\nmode: " ++ mode TProjectViewData mode urd -> "TProjectViewData: " - ++ (Result.map TI.showServerResponse urd - |> Result.mapError Util.httpErrorString - |> (\r -> - case r of - Ok m -> - "message: " ++ m - - Err e -> - "error: " ++ e - ) - ) + -- ++ (Result.map TP.TmshowServerResponse urd + -- |> Result.mapError Util.httpErrorString + -- |> (\r -> + -- case r of + -- Ok m -> + -- "message: " ++ m + -- Err e -> + -- "error: " ++ e + -- ) + -- ) ++ "\nmode: " ++ mode @@ -677,7 +658,7 @@ viewState size state model = E.map ProjectViewMsg <| ProjectView.view (Util.isJust ld) size model.timezone em -stateLogin : State -> Maybe Data.LoginData +stateLogin : State -> Maybe DataUtil.LoginData stateLogin state = case state of Login _ -> @@ -750,59 +731,59 @@ stateLogin state = Nothing -sendTIMsg : String -> TI.SendMsg -> Cmd Msg -sendTIMsg location msg = - sendTIMsgExp location msg TimeclonkReplyData +sendTcMsg : String -> TP.TcMessageX -> Cmd Msg +sendTcMsg location msg = + sendTcMsgExp location msg TimeclonkReplyData -sendTIMsgExp : String -> TI.SendMsg -> (Result Http.Error TI.ServerResponse -> Msg) -> Cmd Msg -sendTIMsgExp location msg tomsg = +sendTcMsgExp : String -> TP.TcMessageX -> (Result Http.Error TP.TcResponseX -> Msg) -> Cmd Msg +sendTcMsgExp location msg tomsg = Http.post { url = location ++ "/private" - , body = Http.jsonBody (TI.encodeSendMsg msg) - , expect = Http.expectJson tomsg TI.serverResponseDecoder + , body = Http.jsonBody (TP.tcMessageXEncoder msg) + , expect = Http.expectJson tomsg TP.tcResponseXDecoder } -sendPIMsg : String -> PI.SendMsg -> Cmd Msg +sendPIMsg : String -> TP.PublicMessageX -> Cmd Msg sendPIMsg location msg = sendPIMsgExp location msg PublicReplyData -sendPIMsgExp : String -> PI.SendMsg -> (Result Http.Error PI.ServerResponse -> Msg) -> Cmd Msg +sendPIMsgExp : String -> TP.PublicMessageX -> (Result Http.Error TP.PublicResponseX -> Msg) -> Cmd Msg sendPIMsgExp location msg tomsg = Http.post { url = location ++ "/public" - , body = Http.jsonBody (PI.encodeSendMsg msg) - , expect = Http.expectJson tomsg PI.serverResponseDecoder + , body = Http.jsonBody (TP.publicMessageXEncoder msg) + , expect = Http.expectJson tomsg TP.publicResponseXDecoder } -sendUIMsg : String -> UI.SendMsg -> Cmd Msg +sendUIMsg : String -> OD.UserRequest -> Cmd Msg sendUIMsg location msg = sendUIMsgExp location msg UserReplyData -sendUIMsgExp : String -> UI.SendMsg -> (Result Http.Error UI.ServerResponse -> Msg) -> Cmd Msg +sendUIMsgExp : String -> OD.UserRequest -> (Result Http.Error OD.UserResponse -> Msg) -> Cmd Msg sendUIMsgExp location msg tomsg = Http.post { url = location ++ "/user" - , body = Http.jsonBody (UI.encodeSendMsg msg) - , expect = Http.expectJson tomsg UI.serverResponseDecoder + , body = Http.jsonBody (OD.userRequestEncoder msg) + , expect = Http.expectJson tomsg OD.userResponseDecoder } -sendAIMsg : String -> AI.SendMsg -> Cmd Msg +sendAIMsg : String -> OD.AdminRequest -> Cmd Msg sendAIMsg location msg = sendAIMsgExp location msg AdminReplyData -sendAIMsgExp : String -> AI.SendMsg -> (Result Http.Error AI.ServerResponse -> Msg) -> Cmd Msg +sendAIMsgExp : String -> OD.AdminRequest -> (Result Http.Error OD.AdminResponse -> Msg) -> Cmd Msg sendAIMsgExp location msg tomsg = Http.post { url = location ++ "/admin" - , body = Http.jsonBody (AI.encodeSendMsg msg) - , expect = Http.expectJson tomsg AI.serverResponseDecoder + , body = Http.jsonBody (OD.adminRequestEncoder msg) + , expect = Http.expectJson tomsg OD.adminResponseDecoder } @@ -1050,7 +1031,7 @@ displayMessageDialog model message = } -openProjectTime : Model -> String -> Data.ProjectTime -> ( Model, Cmd Msg ) +openProjectTime : Model -> String -> TP.ProjectTime -> ( Model, Cmd Msg ) openProjectTime model mode pt = case stateLogin model.state of Just login -> @@ -1070,13 +1051,13 @@ openProjectTime model mode pt = let obs = case mbrole of - Just Data.Observer -> + Just TP.Observer -> True - Just Data.Member -> + Just TP.Member -> False - Just Data.Admin -> + Just TP.Admin -> False Nothing -> @@ -1134,7 +1115,9 @@ actualupdate msg model = GD.Ok return -> ( { model | state = instate } - , sendUIMsg model.location <| UI.ChangePassword return + , sendUIMsg model.location <| + OD.UrqAuthedRequest <| + OD.AthChangePassword return ) GD.Cancel -> @@ -1147,7 +1130,7 @@ actualupdate msg model = GD.Ok return -> ( { model | state = instate } - , sendUIMsg model.location <| UI.ChangeEmail return + , sendUIMsg model.location <| OD.UrqAuthedRequest <| OD.AthChangeEmail return ) GD.Cancel -> @@ -1162,7 +1145,7 @@ actualupdate msg model = ResetPassword.Ok -> ( { model | state = ResetPassword nst } , sendUIMsg model.location - (UI.SetPassword { uid = nst.userId, newpwd = nst.password, reset_key = nst.reset_key }) + (OD.UrqSetPassword { uid = nst.userId, newpwd = nst.password, resetKey = UUID.toString nst.reset_key }) ) ResetPassword.None -> @@ -1193,14 +1176,14 @@ actualupdate msg model = , Cmd.batch [ Http.post { url = model.location ++ "/invoice" - , body = Http.jsonBody (Data.encodePrintInvoice pi) + , body = Http.jsonBody (TP.printInvoiceEncoder pi) , expect = Http.expectBytesResponse PrintInvoiceReplyData <| resolve <| \bytes -> Ok <| FD.bytes (pi.id ++ ".pdf") "application/pdf" bytes } - , sendTIMsg model.location (TI.SaveProjectInvoice spi) + , sendTcMsg model.location (TP.TmSaveProjectInvoice spi) ] ) @@ -1234,13 +1217,13 @@ actualupdate msg model = UserSettings.LogOut -> ( { model | state = initLoginState model } - , sendUIMsg model.location UI.Logout + , sendUIMsg model.location OD.UrqLogout ) UserSettings.ChangePassword -> ( { model | state = - ChangePasswordDialog (CP.init (Data.ldToOdLd login) Common.buttonStyle (UserSettings.view numod |> E.map (always ()))) + ChangePasswordDialog (CP.init (DataUtil.ldToOdLd login) Common.buttonStyle (UserSettings.view numod |> E.map (always ()))) (UserSettings numod login prevstate) } , Cmd.none @@ -1249,7 +1232,7 @@ actualupdate msg model = UserSettings.ChangeEmail -> ( { model | state = - ChangeEmailDialog (CE.init (Data.ldToOdLd login) Common.buttonStyle (UserSettings.view numod |> E.map (always ()))) + ChangeEmailDialog (CE.init (DataUtil.ldToOdLd login) Common.buttonStyle (UserSettings.view numod |> E.map (always ()))) (UserSettings numod login prevstate) } , Cmd.none @@ -1326,15 +1309,15 @@ actualupdate msg model = Ok uiresponse -> case uiresponse of - TI.ProjectTime x -> + TP.TrProjectTime x -> openProjectTime model mode x - TI.NotLoggedIn -> - ( { model | state = initLoginState model }, Cmd.none ) - - TI.InvalidUserOrPwd -> + TP.TrProjectTimeDenied -> + -- TODO: something else? ( { model | state = initLoginState model }, Cmd.none ) + -- TP.TrInvalidUserOrPwd -> + -- ( { model | state = initLoginState model }, Cmd.none ) _ -> ( unexpectedMsg model msg , Cmd.none @@ -1347,7 +1330,7 @@ actualupdate msg model = Ok uiresponse -> case uiresponse of - PI.ProjectTime x -> + TP.PrProjectTime x -> ( { model | state = ProjectView @@ -1357,11 +1340,6 @@ actualupdate msg model = , Cmd.none ) - _ -> - ( unexpectedMsg model msg - , Cmd.none - ) - ( TProjectViewData mode urd, state ) -> case urd of Err e -> @@ -1369,7 +1347,7 @@ actualupdate msg model = Ok uiresponse -> case uiresponse of - TI.ProjectTime x -> + TP.TrProjectTime x -> ( { model | state = ProjectView @@ -1379,7 +1357,8 @@ actualupdate msg model = , Cmd.none ) - TI.NotLoggedIn -> + TP.TrProjectTimeDenied -> + -- TODO: something else? ( { model | state = initLoginState model }, Cmd.none ) _ -> @@ -1394,19 +1373,19 @@ actualupdate msg model = Ok uiresponse -> case uiresponse of - UI.ServerError e -> + OD.UrpServerError e -> ( displayMessageDialog model <| e, Cmd.none ) - UI.RegistrationSent -> + OD.UrpRegistrationSent -> ( model, Cmd.none ) - UI.LoggedIn login -> + OD.UrpLoggedIn login -> let lgmod = { model | state = ShowMessage { message = "logged in" } - (Data.odLdToLd login) + (DataUtil.odLdToLd login) Nothing } in @@ -1436,10 +1415,10 @@ actualupdate msg model = _ -> initialPage lgmod - UI.LoggedOut -> + OD.UrpLoggedOut -> ( model, Cmd.none ) - UI.ResetPasswordAck -> + OD.UrpResetPasswordAck -> let nmod = { model @@ -1451,7 +1430,7 @@ actualupdate msg model = , Cmd.none ) - UI.SetPasswordAck -> + OD.UrpSetPasswordAck -> let nmod = { model @@ -1463,12 +1442,12 @@ actualupdate msg model = , Cmd.none ) - UI.ChangedPassword -> + OD.UrpChangedPassword -> ( displayMessageDialog model "password changed" , Cmd.none ) - UI.ChangedEmail -> + OD.UrpChangedEmail -> ( displayMessageDialog model <| "email change confirmation sent! check your inbox (or spam folder) for an email with title 'change " ++ model.appname @@ -1476,27 +1455,27 @@ actualupdate msg model = , Cmd.none ) - UI.UserExists -> + OD.UrpUserExists -> case state of Login lmod -> ( { model | state = Login <| Login.userExists lmod }, Cmd.none ) _ -> - ( unexpectedMessage model (UI.showServerResponse uiresponse) + ( unexpectedMessage model (showMessage msg) , Cmd.none ) - UI.UnregisteredUser -> + OD.UrpUnregisteredUser -> case state of Login lmod -> ( { model | state = Login <| Login.unregisteredUser lmod }, Cmd.none ) _ -> - ( unexpectedMessage model (UI.showServerResponse uiresponse) + ( unexpectedMessage model (showMessage msg) , Cmd.none ) - UI.NotLoggedIn -> + OD.UrpNotLoggedIn -> case state of Login lmod -> ( { model | state = Login lmod }, Cmd.none ) @@ -1504,18 +1483,18 @@ actualupdate msg model = _ -> ( { model | state = initLoginState model }, Cmd.none ) - UI.InvalidUserOrPwd -> + OD.UrpInvalidUserOrPwd -> case state of Login lmod -> ( { model | state = Login <| Login.invalidUserOrPwd lmod }, Cmd.none ) _ -> ( unexpectedMessage { model | state = initLoginState model } - (UI.showServerResponse uiresponse) + (showMessage msg) , Cmd.none ) - UI.BlankUserName -> + OD.UrpBlankUserName -> case state of Invited lmod -> ( { model | state = Invited <| Invited.blankUserName lmod }, Cmd.none ) @@ -1525,11 +1504,11 @@ actualupdate msg model = _ -> ( unexpectedMessage { model | state = initLoginState model } - (UI.showServerResponse uiresponse) + (showMessage msg) , Cmd.none ) - UI.BlankPassword -> + OD.UrpBlankPassword -> case state of Invited lmod -> ( { model | state = Invited <| Invited.blankPassword lmod }, Cmd.none ) @@ -1539,11 +1518,11 @@ actualupdate msg model = _ -> ( unexpectedMessage { model | state = initLoginState model } - (UI.showServerResponse uiresponse) + (showMessage msg) , Cmd.none ) - UI.Invite invite -> + OD.UrpInvite invite -> case model.state of InviteUser mdl login -> ( { model @@ -1560,6 +1539,27 @@ actualupdate msg model = , Cmd.none ) + OD.UrpInvalidUserId -> + ( displayMessageDialog model <| "invalid user id", Cmd.none ) + + OD.UrpInvalidUserUuid -> + ( displayMessageDialog model <| "invalid user uuid", Cmd.none ) + + OD.UrpAccountDeactivated -> + ( displayMessageDialog model <| "account deactivated", Cmd.none ) + + OD.UrpChangedRemoteUrl _ -> + ( displayMessageDialog model <| "changed remote url", Cmd.none ) + + OD.UrpRemoteRegistrationFailed -> + ( displayMessageDialog model <| "remote registration failed", Cmd.none ) + + OD.UrpRemoteUser _ -> + ( displayMessageDialog model <| "remote user", Cmd.none ) + + OD.UrpNoData -> + ( displayMessageDialog model <| "no data", Cmd.none ) + ( AdminReplyData ard, state ) -> case ard of Err e -> @@ -1567,7 +1567,7 @@ actualupdate msg model = Ok airesponse -> case airesponse of - AI.NotLoggedIn -> + OD.ArpNotLoggedIn -> case state of Login lmod -> ( { model | state = Login lmod }, Cmd.none ) @@ -1575,7 +1575,7 @@ actualupdate msg model = _ -> ( { model | state = initLoginState model }, Cmd.none ) - AI.Users users -> + OD.ArpUsers users -> case stateLogin model.state of Just login -> ( { model | state = UserListing (UserListing.init users) login }, Cmd.none ) @@ -1583,12 +1583,12 @@ actualupdate msg model = Nothing -> ( displayMessageDialog model "not logged in", Cmd.none ) - AI.UserDeleted id -> + OD.ArpUserDeleted id -> ( displayMessageDialog model "user deleted!" - , sendAIMsg model.location AI.GetUsers + , sendAIMsg model.location OD.ArqGetUsers ) - AI.UserUpdated ld -> + OD.ArpUserUpdated ld -> case model.state of UserEdit ue login -> ( displayMessageDialog { model | state = UserEdit (UserEdit.onUserUpdated ue ld) login } "user updated" @@ -1598,7 +1598,7 @@ actualupdate msg model = _ -> ( model, Cmd.none ) - AI.UserInvite ui -> + OD.ArpUserInvite ui -> case stateLogin model.state of Just login -> ( { model @@ -1615,7 +1615,7 @@ actualupdate msg model = , Cmd.none ) - AI.PwdReset pr -> + OD.ArpPwdReset pr -> case state of UserEdit uem login -> ( { model @@ -1630,7 +1630,22 @@ actualupdate msg model = _ -> ( model, Cmd.none ) - AI.ServerError e -> + OD.ArpUserNotDeleted _ -> + ( displayMessageDialog model "user not deleted", Cmd.none ) + + OD.ArpNoUserId -> + ( displayMessageDialog model "no user id", Cmd.none ) + + OD.ArpNoData -> + ( displayMessageDialog model "no data", Cmd.none ) + + OD.ArpInvalidUserOrPassword -> + ( displayMessageDialog model "invalid user or password", Cmd.none ) + + OD.ArpAccessDenied -> + ( displayMessageDialog model "access denied", Cmd.none ) + + OD.ArpServerError e -> ( displayMessageDialog model <| e, Cmd.none ) ( UserListingMsg umsg, UserListing umod login ) -> @@ -1645,7 +1660,7 @@ actualupdate msg model = UserListing.InviteUser -> -- get project listing, so we can add users to a specific project. ( model - , sendTIMsg model.location <| TI.GetProjectList login.userid + , sendTcMsg model.location <| TP.TmGetProjectList ) UserListing.EditUser ld -> @@ -1662,22 +1677,22 @@ actualupdate msg model = case c of UserEdit.Done -> ( model - , sendAIMsg model.location AI.GetUsers + , sendAIMsg model.location OD.ArqGetUsers ) UserEdit.Delete id -> ( model - , sendAIMsg model.location <| AI.DeleteUser id + , sendAIMsg model.location <| OD.ArqDeleteUser id ) UserEdit.ResetPwd id -> ( model - , sendAIMsg model.location <| AI.GetPwdReset id + , sendAIMsg model.location <| OD.ArqGetPwdReset id ) UserEdit.Save ld -> ( model - , sendAIMsg model.location <| AI.UpdateUser ld + , sendAIMsg model.location <| OD.ArqUpdateUser ld ) UserEdit.None -> @@ -1691,7 +1706,7 @@ actualupdate msg model = case c of UserTime.Done -> ( model - , sendTIMsg model.location <| TI.GetProjectList login.userid + , sendTcMsg model.location <| TP.TmGetProjectList ) UserTime.SaveCsv filename csvstring -> @@ -1702,7 +1717,7 @@ actualupdate msg model = UserTime.Settings -> ( { model | state = - UserSettings (UserSettings.init (Data.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state + UserSettings (UserSettings.init (DataUtil.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state } , Cmd.none ) @@ -1722,10 +1737,10 @@ actualupdate msg model = ShowUrl.Done -> ( model , if login.admin then - sendAIMsg model.location AI.GetUsers + sendAIMsg model.location OD.ArqGetUsers else - sendTIMsg model.location <| TI.GetProjectList login.userid + sendTcMsg model.location <| TP.TmGetProjectList ) ShowUrl.None -> @@ -1738,16 +1753,23 @@ actualupdate msg model = Ok uiresponse -> case uiresponse of - TI.ServerError e -> - ( displayMessageDialog model <| e, Cmd.none ) + -- TP.TrServerError e -> + -- ( displayMessageDialog model <| e, Cmd.none ) + -- TP.TrNotLoggedIn -> + -- ( { model | state = initLoginState model }, Cmd.none ) + TP.TrProjectEditDenied -> + ( displayMessageDialog model <| "Project Edit Denied", Cmd.none ) - TI.NotLoggedIn -> - ( { model | state = initLoginState model }, Cmd.none ) + TP.TrSavedProjectEditDenied -> + ( displayMessageDialog model <| "Saved Project Edit Denied", Cmd.none ) - TI.InvalidUserOrPwd -> - ( { model | state = initLoginState model }, Cmd.none ) + TP.TrSavedProjectInvoiceDenied -> + ( displayMessageDialog model <| "Saved Project Invoice Denied", Cmd.none ) + + TP.TrProjectTimeDenied -> + ( displayMessageDialog model <| "Project Time Denied", Cmd.none ) - TI.ProjectList x -> + TP.TrProjectList x -> case state of UserListing ulst login -> ( { model | state = InviteUser (InviteUser.init x login) login }, Cmd.none ) @@ -1760,7 +1782,7 @@ actualupdate msg model = Nothing -> ( model, Cmd.none ) - TI.ProjectEdit x -> + TP.TrProjectEdit x -> case stateLogin state of Just login -> ( { model | state = ProjectEdit (ProjectEdit.initEdit x.project x.members) login }, Cmd.none ) @@ -1768,7 +1790,7 @@ actualupdate msg model = Nothing -> ( model, Cmd.none ) - TI.ProjectTime x -> + TP.TrProjectTime x -> case state of ProjectTime st login -> ( { model | state = ProjectTime (ProjectTime.onProjectTime model.timezone login x st) login }, Cmd.none ) @@ -1776,7 +1798,7 @@ actualupdate msg model = _ -> openProjectTime model "" x - TI.UserTime telist -> + TP.TrUserTime telist -> case state of ProjectListing plst ld -> ( { model @@ -1786,7 +1808,7 @@ actualupdate msg model = ld telist (plst.projects - |> List.map (\p -> ( Data.getProjectIdVal p.id, p )) + |> List.map (\p -> ( DataUtil.getProjectIdVal p.id, p )) |> Dict.fromList ) model.pageincrement @@ -1799,7 +1821,7 @@ actualupdate msg model = _ -> ( model, Cmd.none ) - TI.SavedProjectInvoice p -> + TP.TrSavedProjectInvoice p -> case state of ProjectEdit s l -> ( { model | state = ProjectEdit (ProjectEdit.onSavedProjectInvoice p s) l }, Cmd.none ) @@ -1810,7 +1832,7 @@ actualupdate msg model = _ -> ( model, Cmd.none ) - TI.SavedProjectEdit x -> + TP.TrSavedProjectEdit x -> case state of ProjectEdit s l -> ( { model | state = ProjectEdit (ProjectEdit.onSavedProjectEdit x s) l }, Cmd.none ) @@ -1818,7 +1840,7 @@ actualupdate msg model = _ -> ( model, Cmd.none ) - TI.AllUsers x -> + TP.TrAllUsers x -> case state of ProjectEdit s l -> let @@ -1840,6 +1862,7 @@ actualupdate msg model = { choices = somems |> TDict.values |> List.map (\m -> ( m, m.name )) , selected = Nothing , search = "" + , mobile = False } Common.buttonStyle (E.map (always ()) (ProjectEdit.view l model.size s)) @@ -1852,6 +1875,9 @@ actualupdate msg model = _ -> ( model, Cmd.none ) + TP.TrError tce -> + ( displayMessageDialog model <| DataUtil.showTimeClonkError tce, Cmd.none ) + ( PublicReplyData urd, state ) -> case urd of Err e -> @@ -1859,10 +1885,7 @@ actualupdate msg model = Ok uiresponse -> case uiresponse of - PI.ServerError e -> - ( displayMessageDialog model <| e, Cmd.none ) - - PI.ProjectTime x -> + TP.PrProjectTime x -> case state of ProjectView st mblogin -> ( { model | state = ProjectView (ProjectView.onProjectTime model.timezone x st) mblogin }, Cmd.none ) @@ -1906,7 +1929,7 @@ actualupdate msg model = GD.Ok return -> case instate of ProjectEdit pemod login -> - ( { model | state = ProjectEdit (ProjectEdit.addMember return Data.Member pemod) login } + ( { model | state = ProjectEdit (ProjectEdit.addMember return TP.Member pemod) login } , Cmd.none ) @@ -1957,11 +1980,6 @@ actualupdate msg model = ProjectListing.update ms st login in case cmd of - ProjectListing.Selected id -> - ( { model | state = ProjectListing nm login } - , sendTIMsg model.location <| TI.GetProjectEdit id - ) - ProjectListing.New -> ( { model | state = ProjectEdit (ProjectEdit.initNew login) login } , Cmd.none @@ -1977,8 +1995,8 @@ actualupdate msg model = millis = Time.posixToMillis now in - sendTIMsg model.location <| - TI.GetUserTime + sendTcMsg model.location <| + TP.TmGetUserTime ) ) ) @@ -1989,14 +2007,14 @@ actualupdate msg model = ProjectListing.Settings -> ( { model | state = - UserSettings (UserSettings.init (Data.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state + UserSettings (UserSettings.init (DataUtil.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state } , Cmd.none ) ProjectListing.Admin -> ( model - , sendAIMsg model.location AI.GetUsers + , sendAIMsg model.location OD.ArqGetUsers ) ProjectListing.Invite -> @@ -2038,12 +2056,12 @@ actualupdate msg model = ) -handleProjectEdit : Model -> ( ProjectEdit.Model, ProjectEdit.Command ) -> Data.LoginData -> ( Model, Cmd Msg ) +handleProjectEdit : Model -> ( ProjectEdit.Model, ProjectEdit.Command ) -> DataUtil.LoginData -> ( Model, Cmd Msg ) handleProjectEdit model ( nm, cmd ) login = case cmd of ProjectEdit.Save s -> ( { model | state = ProjectEdit nm login } - , sendTIMsg model.location <| TI.SaveProjectEdit s + , sendTcMsg model.location <| TP.TmSaveProjectEdit s ) ProjectEdit.New -> @@ -2053,7 +2071,7 @@ handleProjectEdit model ( nm, cmd ) login = ProjectEdit.AddMember -> ( { model | state = ProjectEdit nm login } - , sendTIMsg model.location <| TI.GetAllUsers + , sendTcMsg model.location <| TP.TmGetAllUsers ) ProjectEdit.SelectRole id -> @@ -2062,13 +2080,14 @@ handleProjectEdit model ( nm, cmd ) login = SelectRoleDialog (SS.init { choices = - [ Data.Member - , Data.Admin - , Data.Observer + [ TP.Member + , TP.Admin + , TP.Observer ] - |> List.map (\r -> ( ( id, r ), Data.showRole r )) + |> List.map (\r -> ( ( id, r ), DataUtil.showRole r )) , selected = Nothing , search = "" + , mobile = False } Common.buttonStyle (E.map (always ()) (ProjectEdit.view login model.size nm)) @@ -2080,13 +2099,13 @@ handleProjectEdit model ( nm, cmd ) login = ProjectEdit.Done -> ( { model | state = ProjectEdit nm login } - , sendTIMsg model.location <| TI.GetProjectList login.userid + , sendTcMsg model.location <| TP.TmGetProjectList ) ProjectEdit.Settings -> ( { model | state = - UserSettings (UserSettings.init (Data.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state + UserSettings (UserSettings.init (DataUtil.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state } , Cmd.none ) @@ -2095,12 +2114,12 @@ handleProjectEdit model ( nm, cmd ) login = ( { model | state = ProjectEdit nm login }, Cmd.none ) -handleProjectTime : Model -> ( ProjectTime.Model, ProjectTime.Command ) -> Data.LoginData -> ( Model, Cmd Msg ) +handleProjectTime : Model -> ( ProjectTime.Model, ProjectTime.Command ) -> DataUtil.LoginData -> ( Model, Cmd Msg ) handleProjectTime model ( nm, cmd ) login = case cmd of ProjectTime.Save s -> ( { model | state = ProjectTime nm login } - , sendTIMsg model.location <| TI.SaveProjectTime s + , sendTcMsg model.location <| TP.TmSaveProjectTime s ) ProjectTime.Edit -> @@ -2115,13 +2134,13 @@ handleProjectTime model ( nm, cmd ) login = ProjectTime.Done -> ( { model | state = ProjectTime nm login } - , sendTIMsg model.location <| TI.GetProjectList login.userid + , sendTcMsg model.location <| TP.TmGetProjectList ) ProjectTime.Settings -> ( { model | state = - UserSettings (UserSettings.init (Data.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state + UserSettings (UserSettings.init (DataUtil.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state } , Cmd.none ) @@ -2147,6 +2166,7 @@ handleProjectTime model ( nm, cmd ) login = { choices = members |> List.map (\m -> ( m, m.name )) , selected = Nothing , search = "" + , mobile = False } Common.buttonStyle (E.map (always ()) (ProjectTime.view login model.size model.timezone nm)) @@ -2168,11 +2188,11 @@ handleProjectTime model ( nm, cmd ) login = |> Task.perform (\now -> PrintInvoiceInit pi - (Data.piDate now model.timezone) + (DataUtil.piDate now model.timezone) (pi.duedays |> Maybe.map (\dd -> - Data.piDate + DataUtil.piDate (now |> Time.posixToMillis |> (+) (dd * 24 * 60 * 60 * 1000) @@ -2205,14 +2225,14 @@ resolve toResult response = Result.mapError Http.BadBody (toResult body) -handleProjectView : Model -> ( ProjectView.Model, ProjectView.Command ) -> Maybe Data.LoginData -> ( Model, Cmd Msg ) +handleProjectView : Model -> ( ProjectView.Model, ProjectView.Command ) -> Maybe DataUtil.LoginData -> ( Model, Cmd Msg ) handleProjectView model ( nm, cmd ) mblogin = case cmd of ProjectView.Done -> case mblogin of Just login -> ( { model | state = ProjectView nm mblogin } - , sendTIMsg model.location <| TI.GetProjectList login.userid + , sendTcMsg model.location <| TP.TmGetProjectList ) Nothing -> @@ -2223,7 +2243,7 @@ handleProjectView model ( nm, cmd ) mblogin = Just login -> ( { model | state = - UserSettings (UserSettings.init (Data.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state + UserSettings (UserSettings.init (DataUtil.ldToOdLd login) model.fontsize model.saveonclonk model.pageincrement) login model.state } , Cmd.none ) @@ -2252,10 +2272,11 @@ handleLogin model ( lmod, lcmd ) = Login.Register -> ( { model | state = Login lmod } , sendUIMsg model.location - (UI.Register + (OD.UrqRegister { uid = lmod.userId , pwd = lmod.password , email = lmod.email + , remoteUrl = lmod.remoteUrl } ) ) @@ -2263,7 +2284,7 @@ handleLogin model ( lmod, lcmd ) = Login.Login -> ( { model | state = Login lmod } , sendUIMsg model.location <| - UI.Login + OD.UrqLogin { uid = lmod.userId , pwd = lmod.password } @@ -2272,7 +2293,7 @@ handleLogin model ( lmod, lcmd ) = Login.Reset -> ( { model | state = Login lmod } , sendUIMsg model.location <| - UI.ResetPassword + OD.UrqResetPassword { uid = lmod.userId } ) @@ -2287,7 +2308,7 @@ handleInvited model ( lmod, lcmd ) = Invited.RSVP -> ( { model | state = Invited lmod } , sendUIMsg model.location - (UI.RSVP + (OD.UrqRsvp { uid = lmod.userId , pwd = lmod.password , email = lmod.email @@ -2297,7 +2318,7 @@ handleInvited model ( lmod, lcmd ) = ) -handleInviteUser : Model -> ( InviteUser.Model, InviteUser.Command ) -> Data.LoginData -> ( Model, Cmd Msg ) +handleInviteUser : Model -> ( InviteUser.Model, InviteUser.Command ) -> DataUtil.LoginData -> ( Model, Cmd Msg ) handleInviteUser model ( lmod, lcmd ) ld = case lcmd of InviteUser.None -> @@ -2307,17 +2328,17 @@ handleInviteUser model ( lmod, lcmd ) ld = ( { model | state = InviteUser lmod ld } , if ld.admin then sendAIMsg model.location - (AI.GetInvite invite) + (OD.ArqGetInvite invite) else sendUIMsg model.location - (UI.GetInvite invite) + (OD.UrqAuthedRequest <| OD.AthGetInvite invite) ) InviteUser.Cancel -> ( model , sendAIMsg model.location - AI.GetUsers + OD.ArqGetUsers ) @@ -2348,7 +2369,7 @@ initialPage curmodel = ( { curmodel | state = ShowMessage { message = "congrats, you are logged in!" } login Nothing } - , sendTIMsg curmodel.location <| TI.GetProjectList login.userid + , sendTcMsg curmodel.location <| TP.TmGetProjectList ) Nothing -> @@ -2375,12 +2396,14 @@ init flags url key zone fontsize saveonclonk pageincrement = flags.adminsettings |> Maybe.andThen (\v -> - JD.decodeValue OD.decodeAdminSettings v + JD.decodeValue OD.adminSettingsDecoder v |> Result.toMaybe ) |> Maybe.withDefault { openRegistration = False - , nonAdminInvite = False + , sendEmails = False + , nonAdminInvite = False -- TODO true? + , remoteRegistration = False } imodel = @@ -2391,10 +2414,10 @@ init flags url key zone fontsize saveonclonk pageincrement = Just v -> case - JD.decodeValue OD.decodeLoginData v + JD.decodeValue OD.loginDataDecoder v of Ok l -> - ShowMessage { message = "loading..." } (Data.odLdToLd l) Nothing + ShowMessage { message = "loading..." } (DataUtil.odLdToLd l) Nothing Err e -> PubShowMessage { message = JD.errorToString e } Nothing diff --git a/elm/src/PrintInvoice.elm b/elm/src/PrintInvoice.elm index 221d8dc..a994548 100644 --- a/elm/src/PrintInvoice.elm +++ b/elm/src/PrintInvoice.elm @@ -1,6 +1,8 @@ module PrintInvoice exposing (GDModel, Model, Msg(..), init, update, view) -import Data +-- import Data + +import DataUtil import Dict exposing (Dict) import Element as E exposing (Element) import Element.Background as EBk @@ -10,6 +12,7 @@ import Element.Input as EI import GenDialog as GD import Orgauth.Data as Data import TcCommon as TC +import TcProtocol as TP import Util @@ -17,8 +20,8 @@ type alias Model = { date : String , duedate : String , sequence : Int - , extraFields : List Data.ExtraField - , printInvoiceInternal : Data.PrintInvoiceInternal + , extraFields : List TP.ExtraField + , printInvoiceInternal : DataUtil.PrintInvoiceInternal } @@ -36,10 +39,10 @@ type Msg type alias GDModel = - GD.Model Model Msg ( Data.PrintInvoice, Data.SaveProjectInvoice ) + GD.Model Model Msg ( TP.PrintInvoice, TP.SaveProjectInvoice ) -init : Data.PrintInvoiceInternal -> String -> String -> List (E.Attribute Msg) -> Element () -> GDModel +init : DataUtil.PrintInvoiceInternal -> String -> String -> List (E.Attribute Msg) -> Element () -> GDModel init pi date duedate buttonStyle underLay = { view = view buttonStyle , update = update @@ -64,7 +67,7 @@ view buttonStyle mbsize model = [ E.el [ EF.size 20, EF.bold, E.centerX ] <| E.text "Print Invoice" , E.row [] [ E.text "invoice id: " - , E.el [ EF.bold ] <| E.text (Data.makeInvoiceId model.printInvoiceInternal.idtemplate model.date model.sequence) + , E.el [ EF.bold ] <| E.text (DataUtil.makeInvoiceId model.printInvoiceInternal.idtemplate model.date model.sequence) ] , EI.text [] @@ -154,7 +157,7 @@ view buttonStyle mbsize model = ] -update : Msg -> Model -> GD.Transition Model ( Data.PrintInvoice, Data.SaveProjectInvoice ) +update : Msg -> Model -> GD.Transition Model ( TP.PrintInvoice, TP.SaveProjectInvoice ) update msg model = case msg of DateChanged s -> @@ -231,7 +234,7 @@ update msg model = mpiis = { mpii | seq = model.sequence, extraFields = model.extraFields } in - GD.Ok ( Data.toPi mpiis model.date model.duedate, Data.toSaveProjectInvoice mpiis ) + GD.Ok ( DataUtil.toPi mpiis model.date model.duedate, DataUtil.toSaveProjectInvoice mpiis ) Noop -> GD.Dialog model diff --git a/elm/src/ProjectEdit.elm b/elm/src/ProjectEdit.elm index b89ce75..685a18d 100644 --- a/elm/src/ProjectEdit.elm +++ b/elm/src/ProjectEdit.elm @@ -1,17 +1,19 @@ module ProjectEdit exposing (..) import Common -import Data +import DataUtil import Element as E exposing (Element) import Element.Background as EBk import Element.Border as EBd import Element.Font as EF import Element.Input as EI -import Orgauth.Data exposing (UserId, getUserIdVal, makeUserId) +import Orgauth.Data exposing (UserId) +import Orgauth.UserId exposing (getUserIdVal, makeUserId) import Route import TDict exposing (TDict) import TangoColors as TC import TcCommon as TC +import TcProtocol as TP import Toop import Util import WindowKeys as WK @@ -40,11 +42,11 @@ type Msg type alias Model = - { id : Maybe Data.ProjectId + { id : Maybe TP.ProjectId , name : String , description : String , dueDays : Maybe Int - , extraFields : List Data.ExtraField + , extraFields : List TP.ExtraField , invoiceIdTemplate : String , invoiceSeq : Int , payer : String @@ -55,14 +57,14 @@ type alias Model = , currency : String , createdate : Maybe Int , changeddate : Maybe Int - , members : TDict UserId Int Data.ProjectMember - , initialProject : Maybe Data.Project - , initialMembers : TDict UserId Int Data.ProjectMember + , members : TDict UserId Int TP.ProjectMember + , initialProject : Maybe TP.Project + , initialMembers : TDict UserId Int TP.ProjectMember } type Command - = Save Data.SaveProjectEdit + = Save TP.SaveProjectEdit | New | AddMember | SelectRole UserId @@ -71,7 +73,7 @@ type Command | None -onWkKeyPress : WK.Key -> Model -> Data.LoginData -> ( Model, Command ) +onWkKeyPress : WK.Key -> Model -> DataUtil.LoginData -> ( Model, Command ) onWkKeyPress key model ld = case Toop.T4 key.key key.ctrl key.alt key.shift of Toop.T4 "s" True False False -> @@ -85,7 +87,7 @@ onWkKeyPress key model ld = ( model, None ) -toSaveProject : Model -> Data.SaveProject +toSaveProject : Model -> TP.SaveProject toSaveProject model = let ( r, c ) = @@ -122,7 +124,7 @@ toSaveProject model = } -toSaveProjectEdit : Model -> Data.SaveProjectEdit +toSaveProjectEdit : Model -> TP.SaveProjectEdit toSaveProjectEdit model = { project = toSaveProject model , members = @@ -153,12 +155,12 @@ toSaveProjectEdit model = } -emptyUmDict : TDict UserId Int Data.ProjectMember +emptyUmDict : TDict UserId Int TP.ProjectMember emptyUmDict = TDict.empty getUserIdVal makeUserId -onSavedProjectInvoice : Data.Project -> Model -> Model +onSavedProjectInvoice : TP.Project -> Model -> Model onSavedProjectInvoice project model = let members = @@ -167,7 +169,7 @@ onSavedProjectInvoice project model = initEdit project members -onSavedProjectEdit : Data.SavedProjectEdit -> Model -> Model +onSavedProjectEdit : TP.SavedProjectEdit -> Model -> Model onSavedProjectEdit spe model = let mbrs = @@ -186,12 +188,12 @@ onSavedProjectEdit spe model = } -addMember : Data.User -> Data.Role -> Model -> Model +addMember : TP.User -> TP.Role -> Model -> Model addMember pm role model = { model | members = TDict.insert pm.id { id = pm.id, name = pm.name, role = role } model.members } -setRole : ( UserId, Data.Role ) -> Model -> Model +setRole : ( UserId, TP.Role ) -> Model -> Model setRole ( id, role ) model = { model | members = @@ -239,7 +241,7 @@ isDirty model = projdirty || membersdirty -initNew : Data.LoginData -> Model +initNew : DataUtil.LoginData -> Model initNew ld = { id = Nothing , name = "" @@ -256,13 +258,13 @@ initNew ld = , currency = "" , createdate = Nothing , changeddate = Nothing - , members = TDict.insert ld.userid { id = ld.userid, name = ld.name, role = Data.Admin } emptyUmDict + , members = TDict.insert ld.userid { id = ld.userid, name = ld.name, role = TP.Admin } emptyUmDict , initialProject = Nothing , initialMembers = emptyUmDict } -initEdit : Data.Project -> List Data.ProjectMember -> Model +initEdit : TP.Project -> List TP.ProjectMember -> Model initEdit proj members = let mbs = @@ -291,7 +293,7 @@ initEdit proj members = } -view : Data.LoginData -> Util.Size -> Model -> Element Msg +view : DataUtil.LoginData -> Util.Size -> Model -> Element Msg view ld size model = let maxwidth = @@ -388,7 +390,7 @@ view ld size model = ( True, Just id ) -> let u = - Route.routeUrl <| Route.ProjectViewR (Data.getProjectIdVal id) "team" + Route.routeUrl <| Route.ProjectViewR (DataUtil.getProjectIdVal id) "team" in E.link Common.linkStyle { url = u @@ -560,7 +562,7 @@ view ld size model = \m -> EI.button Common.buttonStyle { onPress = Just <| SelectRolePress m.id - , label = E.text (Data.showRole m.role) + , label = E.text (DataUtil.showRole m.role) } } ] @@ -569,7 +571,7 @@ view ld size model = ] -update : Msg -> Model -> Data.LoginData -> ( Model, Command ) +update : Msg -> Model -> DataUtil.LoginData -> ( Model, Command ) update msg model ld = case msg of NameChanged t -> diff --git a/elm/src/ProjectListing.elm b/elm/src/ProjectListing.elm index cc786e1..faa207d 100644 --- a/elm/src/ProjectListing.elm +++ b/elm/src/ProjectListing.elm @@ -1,7 +1,7 @@ module ProjectListing exposing (..) import Common -import Data +import DataUtil import Dialog as D import Element as E exposing (Element) import Element.Background as EBk @@ -14,14 +14,14 @@ import ProjectTime import Route import TangoColors as TC import TcCommon as TC +import TcProtocol as TP import Toop import Util import WindowKeys as WK type Msg - = SelectPress Int - | NewPress + = NewPress | UserTimePress | DonePress | SettingsPress @@ -30,13 +30,12 @@ type Msg type alias Model = - { projects : List Data.ListProject + { projects : List TP.ListProject } type Command - = Selected Int - | New + = New | UserTime | Done | Settings @@ -45,12 +44,12 @@ type Command | None -init : List Data.ListProject -> Model +init : List TP.ListProject -> Model init projects = { projects = projects } -view : OD.AdminSettings -> Data.LoginData -> Util.Size -> Model -> Element Msg +view : OD.AdminSettings -> DataUtil.LoginData -> Util.Size -> Model -> Element Msg view adminSettings ld size model = let maxwidth = @@ -121,7 +120,7 @@ view adminSettings ld size model = [ E.height <| E.px 30 ] { url = Route.routeUrl - (Route.ProjectTimeR (Data.getProjectIdVal n.id) + (Route.ProjectTimeR (DataUtil.getProjectIdVal n.id) (ProjectTime.showViewMode ProjectTime.Clonks) ) , label = E.text n.name @@ -134,14 +133,9 @@ view adminSettings ld size model = ] -update : Msg -> Model -> Data.LoginData -> ( Model, Command ) +update : Msg -> Model -> DataUtil.LoginData -> ( Model, Command ) update msg model ld = case msg of - SelectPress id -> - ( model - , Selected id - ) - NewPress -> ( model, New ) diff --git a/elm/src/ProjectTime.elm b/elm/src/ProjectTime.elm index 36b0391..fcb8fc9 100644 --- a/elm/src/ProjectTime.elm +++ b/elm/src/ProjectTime.elm @@ -1,9 +1,11 @@ module ProjectTime exposing (..) +-- import Data exposing (InvoiceItem, SaveProjectInvoice) + import Calendar import Common import Csv -import Data exposing (InvoiceItem, SaveProjectInvoice) +import DataUtil import Dict exposing (Dict) import Element as E exposing (Element) import Element.Background as EBk @@ -11,7 +13,8 @@ import Element.Border as EBd import Element.Events as EE import Element.Font as EF import Element.Input as EI -import Orgauth.Data exposing (UserId, getUserIdVal) +import Orgauth.Data exposing (UserId) +import Orgauth.UserId exposing (getUserIdVal) import Paginator as P import Round as R import Set @@ -20,8 +23,18 @@ import TSet exposing (TSet) import TangoColors as TC import TaskSummary as TS import TcCommon as TC +import TcProtocol as TP import Time -import TimeReporting as TR exposing (EditAllocation, EditPayEntry, EditTimeEntry, csvToEditAllocations, csvToEditTimeEntries, descriptionSummary, eteToCsv, millisAsHours) +import TimeReporting as TR + exposing + ( EditAllocation + , EditPayEntry + , EditTimeEntry + , csvToEditAllocations + , csvToEditTimeEntries + , eteToCsv + , millisAsHours + ) import TimeTotaler exposing (TTotaler, getTes, getTotes, mapTimeentry, mkTToteler, setTes) import Toop import Util @@ -82,10 +95,10 @@ type Msg | ClearDistribution | CalcDistribution | ToClipboardMsg String - | PrintInvoiceMsg (List InvoiceItem) + | PrintInvoiceMsg (List TP.InvoiceItem) | OnPaymentChanged UserId String - | AddPaymentPress UserId Int Data.PayType - | AddPayment UserId Int Data.PayType Int + | AddPaymentPress UserId Int TP.PayType + | AddPayment UserId Int TP.PayType Int | DateMsgs (List (Int -> Msg)) Int | CheckAll Bool | CheckItem Int Bool @@ -144,8 +157,8 @@ type FocusColumn type alias Model = - { project : Data.Project - , members : List Data.ProjectMember + { project : TP.Project + , members : List TP.ProjectMember , membernames : Dict Int String , description : String , timeentries : TTotaler @@ -186,7 +199,7 @@ type alias Model = type Command - = Save Data.SaveProjectTime + = Save TP.SaveProjectTime | Edit | Done | GetTime (Int -> Msg) @@ -194,9 +207,9 @@ type Command | SaveCsv String String | Settings | ShowError String - | SelectMember (List Data.User) + | SelectMember (List TP.User) | ToClipboard String - | PrintInvoice Data.PrintInvoiceInternal + | PrintInvoice DataUtil.PrintInvoiceInternal | None @@ -212,7 +225,7 @@ onClockTick time model = } -onSavedProjectInvoice : Data.Project -> Model -> Model +onSavedProjectInvoice : TP.Project -> Model -> Model onSavedProjectInvoice project model = { model | project = project } @@ -280,22 +293,22 @@ readViewMode str = Nothing -emptyTimeEntryIdSet : TSet Data.TimeEntryId Int +emptyTimeEntryIdSet : TSet DataUtil.TimeEntryId Int emptyTimeEntryIdSet = - TSet.empty Data.getTimeEntryIdVal Data.makeTimeEntryId + TSet.empty DataUtil.getTimeEntryIdVal DataUtil.makeTimeEntryId -emptyPayEntryIdSet : TSet Data.PayEntryId Int +emptyPayEntryIdSet : TSet DataUtil.PayEntryId Int emptyPayEntryIdSet = - TSet.empty Data.getPayEntryIdVal Data.makePayEntryId + TSet.empty DataUtil.getPayEntryIdVal DataUtil.makePayEntryId -emptyAllocationIdSet : TSet Data.AllocationId Int +emptyAllocationIdSet : TSet DataUtil.AllocationId Int emptyAllocationIdSet = - TSet.empty Data.getAllocationIdVal Data.makeAllocationId + TSet.empty DataUtil.getAllocationIdVal DataUtil.makeAllocationId -onWkKeyPress : WK.Key -> Model -> Data.LoginData -> Time.Zone -> ( Model, Command ) +onWkKeyPress : WK.Key -> Model -> DataUtil.LoginData -> Time.Zone -> ( Model, Command ) onWkKeyPress key model ld zone = case Toop.T4 key.key key.ctrl key.alt key.shift of Toop.T4 "s" True False False -> @@ -309,7 +322,7 @@ onWkKeyPress key model ld zone = ( model, None ) -toSaveProjectTime : Model -> Data.SaveProjectTime +toSaveProjectTime : Model -> TP.SaveProjectTime toSaveProjectTime model = let savetimeentries = @@ -335,7 +348,8 @@ toSaveProjectTime model = Dict.diff model.initialtimeentries (getTes model.timeentries) |> Dict.values |> List.filterMap .id - |> TSet.insertList emptyTimeEntryIdSet + |> List.map DataUtil.getTimeEntryIdVal + |> Set.fromList savepayentries = model.payentries @@ -360,8 +374,10 @@ toSaveProjectTime model = Dict.diff model.initialpayentries model.payentries |> Dict.values |> List.filterMap .id - |> TSet.insertList emptyPayEntryIdSet + |> List.map DataUtil.getPayEntryIdVal + |> Set.fromList + saveallocations : List TP.SaveAllocation saveallocations = model.allocations |> Dict.values @@ -385,7 +401,8 @@ toSaveProjectTime model = Dict.diff model.initialallocations model.allocations |> Dict.values |> List.filterMap .id - |> TSet.insertList emptyAllocationIdSet + |> List.map DataUtil.getAllocationIdVal + |> Set.fromList in { project = model.project.id , savetimeentries = savetimeentries @@ -395,14 +412,14 @@ toSaveProjectTime model = (\ste dte -> case ste.id of Just id -> - TSet.remove id dte + Set.remove id dte Nothing -> dte ) deletetimeentries savetimeentries - |> TSet.toList + |> Set.toList , savepayentries = savepayentries , deletepayentries = List.foldl @@ -410,14 +427,14 @@ toSaveProjectTime model = (\ste dte -> case ste.id of Just id -> - TSet.remove id dte + Set.remove id dte Nothing -> dte ) deletepayentries savepayentries - |> TSet.toList + |> Set.toList , saveallocations = saveallocations , deleteallocations = List.foldl @@ -425,20 +442,20 @@ toSaveProjectTime model = (\ste dte -> case ste.id of Just id -> - TSet.remove id dte + Set.remove id dte Nothing -> dte ) deleteallocations saveallocations - |> TSet.toList + |> Set.toList } -toEditTimeEntry : Data.TimeEntry -> EditTimeEntry +toEditTimeEntry : TP.TimeEntry -> EditTimeEntry toEditTimeEntry te = - { id = Just te.id + { id = Just (DataUtil.makeTimeEntryId te.id) , user = te.user , description = te.description , startdate = te.startdate @@ -449,9 +466,9 @@ toEditTimeEntry te = } -toEditPayEntry : Data.PayEntry -> EditPayEntry +toEditPayEntry : TP.PayEntry -> EditPayEntry toEditPayEntry te = - { id = Just te.id + { id = Just (DataUtil.makePayEntryId te.id) , user = te.user , description = te.description , paymentdate = te.paymentdate @@ -461,9 +478,9 @@ toEditPayEntry te = } -toEditAllocation : Data.Allocation -> EditAllocation +toEditAllocation : TP.Allocation -> EditAllocation toEditAllocation e = - { id = Just e.id + { id = Just (DataUtil.makeAllocationId e.id) , description = e.description , allocationdate = e.allocationdate , duration = e.duration @@ -471,9 +488,9 @@ toEditAllocation e = } -toSaveTimeEntry : Model -> EditTimeEntry -> Data.SaveTimeEntry +toSaveTimeEntry : Model -> EditTimeEntry -> TP.SaveTimeEntry toSaveTimeEntry model ete = - { id = ete.id + { id = Maybe.map DataUtil.getTimeEntryIdVal ete.id , project = model.project.id , user = ete.user , description = ete.description @@ -483,9 +500,9 @@ toSaveTimeEntry model ete = } -toSavePayEntry : Model -> EditPayEntry -> Data.SavePayEntry +toSavePayEntry : Model -> EditPayEntry -> TP.SavePayEntry toSavePayEntry model ete = - { id = ete.id + { id = Maybe.map DataUtil.getPayEntryIdVal ete.id , project = model.project.id , user = ete.user , description = ete.description @@ -495,9 +512,9 @@ toSavePayEntry model ete = } -toSaveAllocation : Model -> EditAllocation -> Data.SaveAllocation +toSaveAllocation : Model -> EditAllocation -> TP.SaveAllocation toSaveAllocation model e = - { id = e.id + { id = Maybe.map DataUtil.getAllocationIdVal e.id , project = model.project.id , description = e.description , allocationdate = e.allocationdate @@ -505,28 +522,28 @@ toSaveAllocation model e = } -toEteDict : List Data.TimeEntry -> Dict Int EditTimeEntry +toEteDict : List TP.TimeEntry -> Dict Int EditTimeEntry toEteDict te = te |> List.map (toEditTimeEntry >> (\ete -> ( ete.startdate, ete ))) |> Dict.fromList -toEpeDict : List Data.PayEntry -> Dict Int EditPayEntry +toEpeDict : List TP.PayEntry -> Dict Int EditPayEntry toEpeDict pe = pe |> List.map (toEditPayEntry >> (\epe -> ( epe.paymentdate, epe ))) |> Dict.fromList -toEaDict : List Data.Allocation -> Dict Int EditAllocation +toEaDict : List TP.Allocation -> Dict Int EditAllocation toEaDict a = a |> List.map (toEditAllocation >> (\ea -> ( ea.allocationdate, ea ))) |> Dict.fromList -onSavedProjectTime : List Data.TimeEntry -> Model -> Model +onSavedProjectTime : List TP.TimeEntry -> Model -> Model onSavedProjectTime te model = let ietes = @@ -548,7 +565,7 @@ isDirty model = /= model.initialallocations -init : Time.Zone -> Data.LoginData -> Data.ProjectTime -> Bool -> Int -> String -> Model +init : Time.Zone -> DataUtil.LoginData -> TP.ProjectTime -> Bool -> Int -> String -> Model init zone ld pt saveonclonk pageincrement mode = let ietes = @@ -561,7 +578,13 @@ init zone ld pt saveonclonk pageincrement mode = toEaDict pt.allocations description = - ietes |> Dict.toList |> List.filter (\( _, e ) -> e.user == ld.userid) |> List.reverse |> List.head |> Maybe.map (\( _, ete ) -> ete.description) |> Maybe.withDefault "" + ietes + |> Dict.toList + |> List.filter (\( _, e ) -> e.user == ld.userid) + |> List.reverse + |> List.head + |> Maybe.map (\( _, ete ) -> ete.description) + |> Maybe.withDefault "" in { project = pt.project , members = pt.members @@ -631,7 +654,7 @@ setPageIncrement pageincrement model = } -onProjectTime : Time.Zone -> Data.LoginData -> Data.ProjectTime -> Model -> Model +onProjectTime : Time.Zone -> DataUtil.LoginData -> TP.ProjectTime -> Model -> Model onProjectTime zone ld pt model = let nm = @@ -668,7 +691,7 @@ viewModeBar model = ] -view : Data.LoginData -> Util.Size -> Time.Zone -> Model -> Element Msg +view : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> Element Msg view ld size zone model = let maxwidth = @@ -746,7 +769,7 @@ dateTimeWidth = 200 -clonkview : Data.LoginData -> Util.Size -> Time.Zone -> Bool -> Model -> List (Element Msg) +clonkview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Bool -> Model -> List (Element Msg) clonkview ld _ zone isdirty model = let ttotes = @@ -1291,7 +1314,7 @@ clonkview ld _ zone isdirty model = ] -teamview : Data.LoginData -> Util.Size -> Time.Zone -> Bool -> Model -> List (Element Msg) +teamview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Bool -> Model -> List (Element Msg) teamview _ _ zone isdirty model = let ttotes = @@ -1484,14 +1507,14 @@ type Entry | Allocation EditAllocation -taskview : Data.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) +taskview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) taskview ld _ _ model = [ E.map TSMsg <| TS.taskFilterView ld.userid model.tasksummarymod , E.map TSMsg <| TS.taskview model.timeentries model.payentries model.membernames model.tasksummarymod ] -distributionview : Data.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) +distributionview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) distributionview _ _ zone model = let timetotes = @@ -1642,7 +1665,11 @@ distributionview _ _ zone model = [ E.text <| String.fromInt (Calendar.getYear cdate) ++ "/" - ++ (cdate |> Calendar.getMonth |> Calendar.monthToInt |> String.fromInt) + ++ (cdate + |> Calendar.getMonth + |> Calendar.monthToInt + |> String.fromInt + ) ++ "/" ++ String.fromInt (Calendar.getDay cdate) @@ -1744,10 +1771,10 @@ distributionview _ _ zone model = E.text <| s ++ (case epe.paytype of - Data.Invoiced -> + TP.Invoiced -> " inv" - Data.Paid -> + TP.Paid -> " pmt" ) in @@ -1927,7 +1954,7 @@ distributionview _ _ zone model = Just dist -> let md = - model.members |> List.map (\m -> ( m.id, Data.projectMemberToUser m )) |> TDict.insertList TR.emptyUmDict + model.members |> List.map (\m -> ( m.id, DataUtil.projectMemberToUser m )) |> TDict.insertList TR.emptyUmDict dl = TDict.toList dist @@ -2078,10 +2105,10 @@ distributionview _ _ zone model = Just millis -> E.row [ E.spacing TC.defaultSpacing ] [ EI.button Common.buttonStyle - { onPress = Just <| AddPaymentPress user millis Data.Invoiced, label = E.text "invoiced" } + { onPress = Just <| AddPaymentPress user millis TP.Invoiced, label = E.text "invoiced" } |> E.el [ E.centerY ] , EI.button Common.buttonStyle - { onPress = Just <| AddPaymentPress user millis Data.Paid, label = E.text "paid" } + { onPress = Just <| AddPaymentPress user millis TP.Paid, label = E.text "paid" } |> E.el [ E.centerY ] ] @@ -2114,10 +2141,10 @@ distributionview _ _ zone model = in E.row [ E.spacing TC.defaultSpacing, E.padding TC.defaultSpacing ] [ EI.button Common.buttonStyle - { onPress = Just <| DoEet <| makepes Data.Invoiced, label = E.text "invoiced all" } + { onPress = Just <| DoEet <| makepes TP.Invoiced, label = E.text "invoiced all" } |> E.el [ E.centerY ] , EI.button Common.buttonStyle - { onPress = Just <| DoEet <| makepes Data.Paid, label = E.text "paid all" } + { onPress = Just <| DoEet <| makepes TP.Paid, label = E.text "paid all" } |> E.el [ E.centerY ] ] ] @@ -2127,7 +2154,7 @@ distributionview _ _ zone model = ) -allocationview : Data.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) +allocationview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) allocationview ld size zone model = let paytote = @@ -2136,10 +2163,10 @@ allocationview ld size zone model = |> List.foldl (\e t -> case e.paytype of - Data.Paid -> + TP.Paid -> t + e.duration - Data.Invoiced -> + TP.Invoiced -> t ) 0 @@ -2150,10 +2177,10 @@ allocationview ld size zone model = |> List.foldl (\e t -> case e.paytype of - Data.Paid -> + TP.Paid -> t - Data.Invoiced -> + TP.Invoiced -> t + e.duration ) 0 @@ -2460,7 +2487,7 @@ allocationview ld size zone model = ] -payview : Data.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) +payview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) payview ld size zone model = let paytote = @@ -2469,10 +2496,10 @@ payview ld size zone model = |> List.foldl (\e t -> case e.paytype of - Data.Paid -> + TP.Paid -> t + e.duration - Data.Invoiced -> + TP.Invoiced -> t ) 0 @@ -2483,10 +2510,10 @@ payview ld size zone model = |> List.foldl (\e t -> case e.paytype of - Data.Paid -> + TP.Paid -> t - Data.Invoiced -> + TP.Invoiced -> t + e.duration ) 0 @@ -2652,10 +2679,10 @@ payview ld size zone model = E.text <| s ++ (case a.paytype of - Data.Invoiced -> + TP.Invoiced -> " inv" - Data.Paid -> + TP.Paid -> " pmt" ) in @@ -2740,10 +2767,10 @@ payview ld size zone model = ( Just user, Just millis ) -> E.row [ E.spacing TC.defaultSpacing ] [ EI.button Common.buttonStyle - { onPress = Just <| AddPaymentPress user millis Data.Invoiced, label = E.text "invoiced" } + { onPress = Just <| AddPaymentPress user millis TP.Invoiced, label = E.text "invoiced" } |> E.el [ E.centerY ] , EI.button Common.buttonStyle - { onPress = Just <| AddPaymentPress user millis Data.Paid, label = E.text "paid" } + { onPress = Just <| AddPaymentPress user millis TP.Paid, label = E.text "paid" } |> E.el [ E.centerY ] ] @@ -2813,7 +2840,7 @@ payview ld size zone model = ] -update : Msg -> Model -> Data.LoginData -> Time.Zone -> ( Model, Command ) +update : Msg -> Model -> DataUtil.LoginData -> Time.Zone -> ( Model, Command ) update msg model ld zone = case msg of DescriptionChanged t -> @@ -3008,7 +3035,7 @@ update msg model ld zone = SelectPaymentUser -> ( model - , SelectMember (List.map Data.projectMemberToUser model.members) + , SelectMember (List.map DataUtil.projectMemberToUser model.members) ) AllocDescriptionChanged date text -> @@ -3763,8 +3790,8 @@ update msg model ld zone = |> Dict.values |> List.filterMap (\pe -> - if pe.paytype == Data.Invoiced && pe.checked then - Just (AddPayment pe.user pe.duration Data.Paid) + if pe.paytype == TP.Invoiced && pe.checked then + Just (AddPayment pe.user pe.duration TP.Paid) else Nothing @@ -3780,7 +3807,7 @@ update msg model ld zone = Dict.map (\_ pe -> if pe.checked then - { pe | paytype = Data.Invoiced } + { pe | paytype = TP.Invoiced } else pe @@ -3838,7 +3865,7 @@ update msg model ld zone = ( model , SaveCsv ("timeclonk-" ++ model.project.name ++ ".csv") (eteToCsv zone - (Dict.fromList [ ( Data.getProjectIdVal model.project.id, model.project.name ) ]) + (Dict.fromList [ ( DataUtil.getProjectIdVal model.project.id, model.project.name ) ]) model.membernames (case model.viewmode of Clonks -> diff --git a/elm/src/ProjectView.elm b/elm/src/ProjectView.elm index a499a5d..2d4d168 100644 --- a/elm/src/ProjectView.elm +++ b/elm/src/ProjectView.elm @@ -1,9 +1,11 @@ module ProjectView exposing (..) +-- import Data + import Calendar import Common import Csv -import Data +import DataUtil import Dict exposing (Dict) import Element as E exposing (Element) import Element.Background as EBk @@ -11,7 +13,8 @@ import Element.Border as EBd import Element.Events as EE import Element.Font as EF import Element.Input as EI -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) +import Orgauth.Data as OD exposing (UserId) +import Orgauth.UserId as UserId exposing (getUserIdVal, makeUserId) import Paginator as P import Round as R import Set @@ -19,6 +22,7 @@ import TDict exposing (TDict) import TSet exposing (TSet) import TangoColors as TC import TcCommon as TC +import TcProtocol as TP import Time import TimeReporting as TR exposing (EditAllocation, EditPayEntry, EditTimeEntry, csvToEditAllocations, csvToEditTimeEntries, eteToCsv, millisAsHours) import TimeTotaler exposing (TTotaler, getTes, getTotes, mapTimeentry, mkTToteler, setTes) @@ -73,8 +77,8 @@ type FocusColumn type alias Model = - { project : Data.Project - , members : List Data.ProjectMember + { project : TP.Project + , members : List TP.ProjectMember , membernames : Dict Int String , teamentries : TTotaler , teampaginator : P.Model Msg @@ -138,24 +142,24 @@ readViewMode str = Nothing -emptyTimeEntryIdSet : TSet Data.TimeEntryId Int +emptyTimeEntryIdSet : TSet DataUtil.TimeEntryId Int emptyTimeEntryIdSet = - TSet.empty Data.getTimeEntryIdVal Data.makeTimeEntryId + TSet.empty DataUtil.getTimeEntryIdVal DataUtil.makeTimeEntryId -emptyPayEntryIdSet : TSet Data.PayEntryId Int +emptyPayEntryIdSet : TSet DataUtil.PayEntryId Int emptyPayEntryIdSet = - TSet.empty Data.getPayEntryIdVal Data.makePayEntryId + TSet.empty DataUtil.getPayEntryIdVal DataUtil.makePayEntryId -emptyAllocationIdSet : TSet Data.AllocationId Int +emptyAllocationIdSet : TSet DataUtil.AllocationId Int emptyAllocationIdSet = - TSet.empty Data.getAllocationIdVal Data.makeAllocationId + TSet.empty DataUtil.getAllocationIdVal DataUtil.makeAllocationId -toEditTimeEntry : Data.TimeEntry -> EditTimeEntry +toEditTimeEntry : TP.TimeEntry -> EditTimeEntry toEditTimeEntry te = - { id = Just te.id + { id = Just (DataUtil.makeTimeEntryId te.id) , user = te.user , description = te.description , startdate = te.startdate @@ -166,9 +170,9 @@ toEditTimeEntry te = } -toEditPayEntry : Data.PayEntry -> EditPayEntry +toEditPayEntry : TP.PayEntry -> EditPayEntry toEditPayEntry te = - { id = Just te.id + { id = Just (DataUtil.makePayEntryId te.id) , user = te.user , description = te.description , duration = te.duration @@ -178,9 +182,9 @@ toEditPayEntry te = } -toEditAllocation : Data.Allocation -> EditAllocation +toEditAllocation : TP.Allocation -> EditAllocation toEditAllocation e = - { id = Just e.id + { id = Just (DataUtil.makeAllocationId e.id) , description = e.description , allocationdate = e.allocationdate , duration = e.duration @@ -188,28 +192,28 @@ toEditAllocation e = } -toEteDict : List Data.TimeEntry -> Dict Int EditTimeEntry +toEteDict : List TP.TimeEntry -> Dict Int EditTimeEntry toEteDict te = te |> List.map (toEditTimeEntry >> (\ete -> ( ete.startdate, ete ))) |> Dict.fromList -toEpeDict : List Data.PayEntry -> Dict Int EditPayEntry +toEpeDict : List TP.PayEntry -> Dict Int EditPayEntry toEpeDict pe = pe |> List.map (toEditPayEntry >> (\epe -> ( epe.paymentdate, epe ))) |> Dict.fromList -toEaDict : List Data.Allocation -> Dict Int EditAllocation +toEaDict : List TP.Allocation -> Dict Int EditAllocation toEaDict a = a |> List.map (toEditAllocation >> (\ea -> ( ea.allocationdate, ea ))) |> Dict.fromList -init : Time.Zone -> Data.ProjectTime -> Int -> String -> Model +init : Time.Zone -> TP.ProjectTime -> Int -> String -> Model init zone pt pageincrement mode = let ietes = @@ -223,7 +227,7 @@ init zone pt pageincrement mode = in { project = pt.project , members = pt.members - , membernames = pt.members |> List.map (\m -> ( getUserIdVal m.id, m.name )) |> Dict.fromList + , membernames = pt.members |> List.map (\m -> ( UserId.getUserIdVal m.id, m.name )) |> Dict.fromList , teamentries = mkTToteler ietes (always True) zone , teampaginator = P.init TeamForward TeamBack TeamToStart TeamToEnd P.End pageincrement , payentries = iepes @@ -261,7 +265,7 @@ setPageIncrement pageincrement model = } -onProjectTime : Time.Zone -> Data.ProjectTime -> Model -> Model +onProjectTime : Time.Zone -> TP.ProjectTime -> Model -> Model onProjectTime zone pt model = let nm = @@ -795,7 +799,7 @@ distributionview size zone model = Just dist -> let md = - model.members |> List.map (\m -> ( m.id, Data.projectMemberToUser m )) |> TDict.insertList TR.emptyUmDict + model.members |> List.map (\m -> ( m.id, DataUtil.projectMemberToUser m )) |> TDict.insertList TR.emptyUmDict in E.table [ E.spacing TC.defaultSpacing, E.width E.fill ] { data = dist |> TDict.toList @@ -1258,7 +1262,7 @@ update msg model zone = , SaveCsv ("timeclonk-" ++ model.project.name ++ ".csv") (eteToCsv zone - (Dict.fromList [ ( Data.getProjectIdVal model.project.id, model.project.name ) ]) + (Dict.fromList [ ( DataUtil.getProjectIdVal model.project.id, model.project.name ) ]) model.membernames (getTes model.teamentries |> Dict.values) ) diff --git a/elm/src/PublicInterface.elm b/elm/src/PublicInterface.elm deleted file mode 100644 index 5d7fc47..0000000 --- a/elm/src/PublicInterface.elm +++ /dev/null @@ -1,64 +0,0 @@ -module PublicInterface exposing - ( SendMsg(..) - , ServerResponse(..) - , encodeSendMsg - , serverResponseDecoder - , showServerResponse - ) - -import Data -import Json.Decode as JD -import Json.Encode as JE -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) - - -type SendMsg - = GetProjectTime Int - - -type ServerResponse - = ServerError String - | ProjectTime Data.ProjectTime - - -showServerResponse : ServerResponse -> String -showServerResponse sr = - case sr of - ServerError _ -> - "ServerError" - - ProjectTime _ -> - "ProjectTime" - - -encodeSendMsg : SendMsg -> JE.Value -encodeSendMsg sm = - case sm of - GetProjectTime pid -> - JE.object - [ ( "what", JE.string "GetProjectTime" ) - , ( "data", JE.int pid ) - ] - - -serverResponseDecoder : JD.Decoder ServerResponse -serverResponseDecoder = - JD.at [ "what" ] - JD.string - |> JD.andThen - (\what -> - case what of - "server error" -> - JD.map ServerError (JD.at [ "content" ] JD.string) - - "projecttime" -> - JD.map ProjectTime (JD.at [ "content" ] Data.decodeProjectTime) - - "projecttime_denied" -> - JD.succeed - (ServerError "'get project time' failed: insufficient user privileges") - - wat -> - JD.succeed - (ServerError ("invalid 'what' from server: " ++ wat)) - ) diff --git a/elm/src/ShowMessage.elm b/elm/src/ShowMessage.elm index 6c5ec1e..b374eed 100644 --- a/elm/src/ShowMessage.elm +++ b/elm/src/ShowMessage.elm @@ -1,6 +1,5 @@ module ShowMessage exposing (..) -import Data import Element as E exposing (Element) import Element.Background as EBk import Element.Border as EBd diff --git a/elm/src/TaskSummary.elm b/elm/src/TaskSummary.elm index a17a035..21cdfb1 100644 --- a/elm/src/TaskSummary.elm +++ b/elm/src/TaskSummary.elm @@ -1,7 +1,6 @@ module TaskSummary exposing (..) import Common -import Data exposing (PayType(..)) import Dict exposing (Dict) import Element as E exposing (Element) import Element.Font as EF diff --git a/elm/src/TcCommon.elm b/elm/src/TcCommon.elm index d5ac285..dc0aa4e 100644 --- a/elm/src/TcCommon.elm +++ b/elm/src/TcCommon.elm @@ -1,6 +1,5 @@ module TcCommon exposing (..) -import Data as D import Element as E exposing (Element) import Element.Background as EBk import Element.Border as EBd diff --git a/elm/src/TcProtocol.elm b/elm/src/TcProtocol.elm new file mode 100644 index 0000000..414c665 --- /dev/null +++ b/elm/src/TcProtocol.elm @@ -0,0 +1,818 @@ + +-- generated by elm_rs + + +module TcProtocol exposing (..) + +import Dict exposing (Dict) +import Http +import Json.Decode +import Json.Encode +import Orgauth.Data exposing (UserId(..), userIdDecoder, userIdEncoder) +import Url.Builder + + +resultEncoder : (e -> Json.Encode.Value) -> (t -> Json.Encode.Value) -> (Result e t -> Json.Encode.Value) +resultEncoder errEncoder okEncoder enum = + case enum of + Ok inner -> + Json.Encode.object [ ( "Ok", okEncoder inner ) ] + Err inner -> + Json.Encode.object [ ( "Err", errEncoder inner ) ] + + +resultDecoder : Json.Decode.Decoder e -> Json.Decode.Decoder t -> Json.Decode.Decoder (Result e t) +resultDecoder errDecoder okDecoder = + Json.Decode.oneOf + [ Json.Decode.map Ok (Json.Decode.field "Ok" okDecoder) + , Json.Decode.map Err (Json.Decode.field "Err" errDecoder) + ] + + +type alias Project = + { id : ProjectId + , name : String + , description : String + , dueDays : Maybe (Int) + , extraFields : List (ExtraField) + , invoiceIdTemplate : String + , invoiceSeq : Int + , payer : String + , payee : String + , genericTask : String + , public : Bool + , rate : Maybe (Float) + , currency : Maybe (String) + , createdate : Int + , changeddate : Int + } + + +projectEncoder : Project -> Json.Encode.Value +projectEncoder struct = + Json.Encode.object + [ ( "id", (projectIdEncoder) struct.id ) + , ( "name", (Json.Encode.string) struct.name ) + , ( "description", (Json.Encode.string) struct.description ) + , ( "due_days", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.int)) struct.dueDays ) + , ( "extra_fields", (Json.Encode.list (extraFieldEncoder)) struct.extraFields ) + , ( "invoice_id_template", (Json.Encode.string) struct.invoiceIdTemplate ) + , ( "invoice_seq", (Json.Encode.int) struct.invoiceSeq ) + , ( "payer", (Json.Encode.string) struct.payer ) + , ( "payee", (Json.Encode.string) struct.payee ) + , ( "generic_task", (Json.Encode.string) struct.genericTask ) + , ( "public", (Json.Encode.bool) struct.public ) + , ( "rate", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.float)) struct.rate ) + , ( "currency", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.string)) struct.currency ) + , ( "createdate", (Json.Encode.int) struct.createdate ) + , ( "changeddate", (Json.Encode.int) struct.changeddate ) + ] + + +type alias ProjectTime = + { project : Project + , members : List (ProjectMember) + , timeentries : List (TimeEntry) + , payentries : List (PayEntry) + , allocations : List (Allocation) + } + + +projectTimeEncoder : ProjectTime -> Json.Encode.Value +projectTimeEncoder struct = + Json.Encode.object + [ ( "project", (projectEncoder) struct.project ) + , ( "members", (Json.Encode.list (projectMemberEncoder)) struct.members ) + , ( "timeentries", (Json.Encode.list (timeEntryEncoder)) struct.timeentries ) + , ( "payentries", (Json.Encode.list (payEntryEncoder)) struct.payentries ) + , ( "allocations", (Json.Encode.list (allocationEncoder)) struct.allocations ) + ] + + +type alias PayEntry = + { id : Int + , project : ProjectId + , user : UserId + , duration : Int + , paytype : PayType + , paymentdate : Int + , description : String + , createdate : Int + , changeddate : Int + , creator : UserId + } + + +payEntryEncoder : PayEntry -> Json.Encode.Value +payEntryEncoder struct = + Json.Encode.object + [ ( "id", (Json.Encode.int) struct.id ) + , ( "project", (projectIdEncoder) struct.project ) + , ( "user", (userIdEncoder) struct.user ) + , ( "duration", (Json.Encode.int) struct.duration ) + , ( "paytype", (payTypeEncoder) struct.paytype ) + , ( "paymentdate", (Json.Encode.int) struct.paymentdate ) + , ( "description", (Json.Encode.string) struct.description ) + , ( "createdate", (Json.Encode.int) struct.createdate ) + , ( "changeddate", (Json.Encode.int) struct.changeddate ) + , ( "creator", (userIdEncoder) struct.creator ) + ] + + +type alias ProjectMember = + { id : UserId + , name : String + , role : Role + } + + +projectMemberEncoder : ProjectMember -> Json.Encode.Value +projectMemberEncoder struct = + Json.Encode.object + [ ( "id", (userIdEncoder) struct.id ) + , ( "name", (Json.Encode.string) struct.name ) + , ( "role", (roleEncoder) struct.role ) + ] + + +type alias Allocation = + { id : Int + , project : ProjectId + , duration : Int + , allocationdate : Int + , description : String + , createdate : Int + , changeddate : Int + , creator : UserId + } + + +allocationEncoder : Allocation -> Json.Encode.Value +allocationEncoder struct = + Json.Encode.object + [ ( "id", (Json.Encode.int) struct.id ) + , ( "project", (projectIdEncoder) struct.project ) + , ( "duration", (Json.Encode.int) struct.duration ) + , ( "allocationdate", (Json.Encode.int) struct.allocationdate ) + , ( "description", (Json.Encode.string) struct.description ) + , ( "createdate", (Json.Encode.int) struct.createdate ) + , ( "changeddate", (Json.Encode.int) struct.changeddate ) + , ( "creator", (userIdEncoder) struct.creator ) + ] + + +type alias TimeEntry = + { id : Int + , project : ProjectId + , user : UserId + , description : String + , startdate : Int + , enddate : Int + , ignore : Bool + , createdate : Int + , changeddate : Int + , creator : UserId + } + + +timeEntryEncoder : TimeEntry -> Json.Encode.Value +timeEntryEncoder struct = + Json.Encode.object + [ ( "id", (Json.Encode.int) struct.id ) + , ( "project", (projectIdEncoder) struct.project ) + , ( "user", (userIdEncoder) struct.user ) + , ( "description", (Json.Encode.string) struct.description ) + , ( "startdate", (Json.Encode.int) struct.startdate ) + , ( "enddate", (Json.Encode.int) struct.enddate ) + , ( "ignore", (Json.Encode.bool) struct.ignore ) + , ( "createdate", (Json.Encode.int) struct.createdate ) + , ( "changeddate", (Json.Encode.int) struct.changeddate ) + , ( "creator", (userIdEncoder) struct.creator ) + ] + + +type TcMessageX + = TmGetProjectList + | TmSaveProjectEdit (SaveProjectEdit) + | TmGetProjectEdit (ProjectId) + | TmSaveProjectInvoice (SaveProjectInvoice) + | TmGetProjectTime (ProjectId) + | TmSaveProjectTime (SaveProjectTime) + | TmGetUserTime + | TmGetAllUsers + + +tcMessageXEncoder : TcMessageX -> Json.Encode.Value +tcMessageXEncoder enum = + case enum of + TmGetProjectList -> + Json.Encode.string "TmGetProjectList" + TmSaveProjectEdit inner -> + Json.Encode.object [ ( "TmSaveProjectEdit", saveProjectEditEncoder inner ) ] + TmGetProjectEdit inner -> + Json.Encode.object [ ( "TmGetProjectEdit", projectIdEncoder inner ) ] + TmSaveProjectInvoice inner -> + Json.Encode.object [ ( "TmSaveProjectInvoice", saveProjectInvoiceEncoder inner ) ] + TmGetProjectTime inner -> + Json.Encode.object [ ( "TmGetProjectTime", projectIdEncoder inner ) ] + TmSaveProjectTime inner -> + Json.Encode.object [ ( "TmSaveProjectTime", saveProjectTimeEncoder inner ) ] + TmGetUserTime -> + Json.Encode.string "TmGetUserTime" + TmGetAllUsers -> + Json.Encode.string "TmGetAllUsers" + +type ProjectId + = Pid (Int) + + +projectIdEncoder : ProjectId -> Json.Encode.Value +projectIdEncoder enum = + case enum of + Pid inner -> + Json.Encode.object [ ( "Pid", Json.Encode.int inner ) ] + +type PublicMessageX + = PmGetProjectTime (ProjectId) + + +publicMessageXEncoder : PublicMessageX -> Json.Encode.Value +publicMessageXEncoder enum = + case enum of + PmGetProjectTime inner -> + Json.Encode.object [ ( "PmGetProjectTime", projectIdEncoder inner ) ] + +type alias PrintInvoice = + { id : String + , payer : String + , payee : String + , items : List (InvoiceItem) + , date : String + , dueDate : Maybe (String) + , extraFields : List (ExtraField) + , currency : String + } + + +printInvoiceEncoder : PrintInvoice -> Json.Encode.Value +printInvoiceEncoder struct = + Json.Encode.object + [ ( "id", (Json.Encode.string) struct.id ) + , ( "payer", (Json.Encode.string) struct.payer ) + , ( "payee", (Json.Encode.string) struct.payee ) + , ( "items", (Json.Encode.list (invoiceItemEncoder)) struct.items ) + , ( "date", (Json.Encode.string) struct.date ) + , ( "due_date", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.string)) struct.dueDate ) + , ( "extra_fields", (Json.Encode.list (extraFieldEncoder)) struct.extraFields ) + , ( "currency", (Json.Encode.string) struct.currency ) + ] + + +type alias InvoiceItem = + { description : String + , duration : Float + , rate : Float + } + + +invoiceItemEncoder : InvoiceItem -> Json.Encode.Value +invoiceItemEncoder struct = + Json.Encode.object + [ ( "description", (Json.Encode.string) struct.description ) + , ( "duration", (Json.Encode.float) struct.duration ) + , ( "rate", (Json.Encode.float) struct.rate ) + ] + + +type Role + = Member + | Admin + | Observer + + +roleEncoder : Role -> Json.Encode.Value +roleEncoder enum = + case enum of + Member -> + Json.Encode.string "Member" + Admin -> + Json.Encode.string "Admin" + Observer -> + Json.Encode.string "Observer" + +type PayType + = Invoiced + | Paid + + +payTypeEncoder : PayType -> Json.Encode.Value +payTypeEncoder enum = + case enum of + Invoiced -> + Json.Encode.string "Invoiced" + Paid -> + Json.Encode.string "Paid" + +type alias ExtraField = + { n : String + , v : String + } + + +extraFieldEncoder : ExtraField -> Json.Encode.Value +extraFieldEncoder struct = + Json.Encode.object + [ ( "n", (Json.Encode.string) struct.n ) + , ( "v", (Json.Encode.string) struct.v ) + ] + + +type alias SaveProjectEdit = + { project : SaveProject + , members : List (SaveProjectMember) + } + + +saveProjectEditEncoder : SaveProjectEdit -> Json.Encode.Value +saveProjectEditEncoder struct = + Json.Encode.object + [ ( "project", (saveProjectEncoder) struct.project ) + , ( "members", (Json.Encode.list (saveProjectMemberEncoder)) struct.members ) + ] + + +type alias SaveProjectMember = + { id : UserId + , delete : Bool + , role : Role + } + + +saveProjectMemberEncoder : SaveProjectMember -> Json.Encode.Value +saveProjectMemberEncoder struct = + Json.Encode.object + [ ( "id", (userIdEncoder) struct.id ) + , ( "delete", (Json.Encode.bool) struct.delete ) + , ( "role", (roleEncoder) struct.role ) + ] + + +type alias SaveProject = + { id : Maybe (ProjectId) + , name : String + , description : String + , dueDays : Maybe (Int) + , extraFields : List (ExtraField) + , invoiceIdTemplate : String + , invoiceSeq : Int + , payer : String + , payee : String + , genericTask : String + , public : Bool + , rate : Maybe (Float) + , currency : Maybe (String) + } + + +saveProjectEncoder : SaveProject -> Json.Encode.Value +saveProjectEncoder struct = + Json.Encode.object + [ ( "id", (Maybe.withDefault Json.Encode.null << Maybe.map (projectIdEncoder)) struct.id ) + , ( "name", (Json.Encode.string) struct.name ) + , ( "description", (Json.Encode.string) struct.description ) + , ( "due_days", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.int)) struct.dueDays ) + , ( "extra_fields", (Json.Encode.list (extraFieldEncoder)) struct.extraFields ) + , ( "invoice_id_template", (Json.Encode.string) struct.invoiceIdTemplate ) + , ( "invoice_seq", (Json.Encode.int) struct.invoiceSeq ) + , ( "payer", (Json.Encode.string) struct.payer ) + , ( "payee", (Json.Encode.string) struct.payee ) + , ( "generic_task", (Json.Encode.string) struct.genericTask ) + , ( "public", (Json.Encode.bool) struct.public ) + , ( "rate", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.float)) struct.rate ) + , ( "currency", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.string)) struct.currency ) + ] + + +type alias SavedProjectEdit = + { project : Project + , members : List (ProjectMember) + } + + +savedProjectEditEncoder : SavedProjectEdit -> Json.Encode.Value +savedProjectEditEncoder struct = + Json.Encode.object + [ ( "project", (projectEncoder) struct.project ) + , ( "members", (Json.Encode.list (projectMemberEncoder)) struct.members ) + ] + + +type alias SaveProjectInvoice = + { id : ProjectId + , invoiceSeq : Int + , extraFields : List (ExtraField) + } + + +saveProjectInvoiceEncoder : SaveProjectInvoice -> Json.Encode.Value +saveProjectInvoiceEncoder struct = + Json.Encode.object + [ ( "id", (projectIdEncoder) struct.id ) + , ( "invoice_seq", (Json.Encode.int) struct.invoiceSeq ) + , ( "extra_fields", (Json.Encode.list (extraFieldEncoder)) struct.extraFields ) + ] + + +type alias SaveProjectTime = + { project : ProjectId + , savetimeentries : List (SaveTimeEntry) + , deletetimeentries : List (Int) + , savepayentries : List (SavePayEntry) + , deletepayentries : List (Int) + , saveallocations : List (SaveAllocation) + , deleteallocations : List (Int) + } + + +saveProjectTimeEncoder : SaveProjectTime -> Json.Encode.Value +saveProjectTimeEncoder struct = + Json.Encode.object + [ ( "project", (projectIdEncoder) struct.project ) + , ( "savetimeentries", (Json.Encode.list (saveTimeEntryEncoder)) struct.savetimeentries ) + , ( "deletetimeentries", (Json.Encode.list (Json.Encode.int)) struct.deletetimeentries ) + , ( "savepayentries", (Json.Encode.list (savePayEntryEncoder)) struct.savepayentries ) + , ( "deletepayentries", (Json.Encode.list (Json.Encode.int)) struct.deletepayentries ) + , ( "saveallocations", (Json.Encode.list (saveAllocationEncoder)) struct.saveallocations ) + , ( "deleteallocations", (Json.Encode.list (Json.Encode.int)) struct.deleteallocations ) + ] + + +type alias SaveTimeEntry = + { id : Maybe (Int) + , project : ProjectId + , user : UserId + , description : String + , startdate : Int + , enddate : Int + , ignore : Bool + } + + +saveTimeEntryEncoder : SaveTimeEntry -> Json.Encode.Value +saveTimeEntryEncoder struct = + Json.Encode.object + [ ( "id", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.int)) struct.id ) + , ( "project", (projectIdEncoder) struct.project ) + , ( "user", (userIdEncoder) struct.user ) + , ( "description", (Json.Encode.string) struct.description ) + , ( "startdate", (Json.Encode.int) struct.startdate ) + , ( "enddate", (Json.Encode.int) struct.enddate ) + , ( "ignore", (Json.Encode.bool) struct.ignore ) + ] + + +type alias SaveAllocation = + { id : Maybe (Int) + , project : ProjectId + , duration : Int + , allocationdate : Int + , description : String + } + + +saveAllocationEncoder : SaveAllocation -> Json.Encode.Value +saveAllocationEncoder struct = + Json.Encode.object + [ ( "id", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.int)) struct.id ) + , ( "project", (projectIdEncoder) struct.project ) + , ( "duration", (Json.Encode.int) struct.duration ) + , ( "allocationdate", (Json.Encode.int) struct.allocationdate ) + , ( "description", (Json.Encode.string) struct.description ) + ] + + +type alias SavePayEntry = + { id : Maybe (Int) + , project : ProjectId + , user : UserId + , duration : Int + , paytype : PayType + , paymentdate : Int + , description : String + } + + +savePayEntryEncoder : SavePayEntry -> Json.Encode.Value +savePayEntryEncoder struct = + Json.Encode.object + [ ( "id", (Maybe.withDefault Json.Encode.null << Maybe.map (Json.Encode.int)) struct.id ) + , ( "project", (projectIdEncoder) struct.project ) + , ( "user", (userIdEncoder) struct.user ) + , ( "duration", (Json.Encode.int) struct.duration ) + , ( "paytype", (payTypeEncoder) struct.paytype ) + , ( "paymentdate", (Json.Encode.int) struct.paymentdate ) + , ( "description", (Json.Encode.string) struct.description ) + ] + + +type TcResponseX + = TrProjectEdit (ProjectEdit) + | TrProjectEditDenied + | TrSavedProjectEdit (SavedProjectEdit) + | TrSavedProjectEditDenied + | TrSavedProjectInvoice (Project) + | TrSavedProjectInvoiceDenied + | TrProjectTime (ProjectTime) + | TrProjectTimeDenied + | TrProjectList (List (ListProject)) + | TrUserTime (List (TimeEntry)) + | TrAllUsers (List (User)) + | TrError (TimeClonkError) + + +tcResponseXDecoder : Json.Decode.Decoder TcResponseX +tcResponseXDecoder = + Json.Decode.oneOf + [ Json.Decode.map TrProjectEdit (Json.Decode.field "TrProjectEdit" (projectEditDecoder)) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "TrProjectEditDenied" -> + Json.Decode.succeed TrProjectEditDenied + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.map TrSavedProjectEdit (Json.Decode.field "TrSavedProjectEdit" (savedProjectEditDecoder)) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "TrSavedProjectEditDenied" -> + Json.Decode.succeed TrSavedProjectEditDenied + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.map TrSavedProjectInvoice (Json.Decode.field "TrSavedProjectInvoice" (projectDecoder)) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "TrSavedProjectInvoiceDenied" -> + Json.Decode.succeed TrSavedProjectInvoiceDenied + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.map TrProjectTime (Json.Decode.field "TrProjectTime" (projectTimeDecoder)) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "TrProjectTimeDenied" -> + Json.Decode.succeed TrProjectTimeDenied + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.map TrProjectList (Json.Decode.field "TrProjectList" (Json.Decode.list (listProjectDecoder))) + , Json.Decode.map TrUserTime (Json.Decode.field "TrUserTime" (Json.Decode.list (timeEntryDecoder))) + , Json.Decode.map TrAllUsers (Json.Decode.field "TrAllUsers" (Json.Decode.list (userDecoder))) + , Json.Decode.map TrError (Json.Decode.field "TrError" (timeClonkErrorDecoder)) + ] + +type PublicResponseX + = PrProjectTime (ProjectTime) + + +publicResponseXDecoder : Json.Decode.Decoder PublicResponseX +publicResponseXDecoder = + Json.Decode.oneOf + [ Json.Decode.map PrProjectTime (Json.Decode.field "PrProjectTime" (projectTimeDecoder)) + ] + +type alias ListProject = + { id : ProjectId + , name : String + , role : Role + } + + +listProjectDecoder : Json.Decode.Decoder ListProject +listProjectDecoder = + Json.Decode.succeed ListProject + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (projectIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "name" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "role" (roleDecoder))) + + +type TimeClonkError + = TeNotLoggedIn + | TeInvalidLogin + | TeOther (String) + + +timeClonkErrorDecoder : Json.Decode.Decoder TimeClonkError +timeClonkErrorDecoder = + Json.Decode.oneOf + [ Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "TeNotLoggedIn" -> + Json.Decode.succeed TeNotLoggedIn + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "TeInvalidLogin" -> + Json.Decode.succeed TeInvalidLogin + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.map TeOther (Json.Decode.field "TeOther" (Json.Decode.string)) + ] + +projectIdDecoder : Json.Decode.Decoder ProjectId +projectIdDecoder = + Json.Decode.oneOf + [ Json.Decode.map Pid (Json.Decode.field "Pid" (Json.Decode.int)) + ] + +projectDecoder : Json.Decode.Decoder Project +projectDecoder = + Json.Decode.succeed Project + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (projectIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "name" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "description" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "due_days" (Json.Decode.nullable (Json.Decode.int)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "extra_fields" (Json.Decode.list (extraFieldDecoder)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "invoice_id_template" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "invoice_seq" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "payer" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "payee" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "generic_task" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "public" (Json.Decode.bool))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "rate" (Json.Decode.nullable (Json.Decode.float)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "currency" (Json.Decode.nullable (Json.Decode.string)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "createdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "changeddate" (Json.Decode.int))) + + +type alias ProjectEdit = + { project : Project + , members : List (ProjectMember) + } + + +projectEditDecoder : Json.Decode.Decoder ProjectEdit +projectEditDecoder = + Json.Decode.succeed ProjectEdit + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "project" (projectDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "members" (Json.Decode.list (projectMemberDecoder)))) + + +projectTimeDecoder : Json.Decode.Decoder ProjectTime +projectTimeDecoder = + Json.Decode.succeed ProjectTime + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "project" (projectDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "members" (Json.Decode.list (projectMemberDecoder)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "timeentries" (Json.Decode.list (timeEntryDecoder)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "payentries" (Json.Decode.list (payEntryDecoder)))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "allocations" (Json.Decode.list (allocationDecoder)))) + + +payEntryDecoder : Json.Decode.Decoder PayEntry +payEntryDecoder = + Json.Decode.succeed PayEntry + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "project" (projectIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "user" (userIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "duration" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "paytype" (payTypeDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "paymentdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "description" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "createdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "changeddate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "creator" (userIdDecoder))) + + +projectMemberDecoder : Json.Decode.Decoder ProjectMember +projectMemberDecoder = + Json.Decode.succeed ProjectMember + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (userIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "name" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "role" (roleDecoder))) + + +allocationDecoder : Json.Decode.Decoder Allocation +allocationDecoder = + Json.Decode.succeed Allocation + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "project" (projectIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "duration" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "allocationdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "description" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "createdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "changeddate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "creator" (userIdDecoder))) + + +savedProjectEditDecoder : Json.Decode.Decoder SavedProjectEdit +savedProjectEditDecoder = + Json.Decode.succeed SavedProjectEdit + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "project" (projectDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "members" (Json.Decode.list (projectMemberDecoder)))) + + +timeEntryDecoder : Json.Decode.Decoder TimeEntry +timeEntryDecoder = + Json.Decode.succeed TimeEntry + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "project" (projectIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "user" (userIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "description" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "startdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "enddate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "ignore" (Json.Decode.bool))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "createdate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "changeddate" (Json.Decode.int))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "creator" (userIdDecoder))) + + +type alias User = + { id : UserId + , name : String + } + + +userDecoder : Json.Decode.Decoder User +userDecoder = + Json.Decode.succeed User + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "id" (userIdDecoder))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "name" (Json.Decode.string))) + + +roleDecoder : Json.Decode.Decoder Role +roleDecoder = + Json.Decode.oneOf + [ Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "Member" -> + Json.Decode.succeed Member + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "Admin" -> + Json.Decode.succeed Admin + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "Observer" -> + Json.Decode.succeed Observer + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + ] + +extraFieldDecoder : Json.Decode.Decoder ExtraField +extraFieldDecoder = + Json.Decode.succeed ExtraField + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "n" (Json.Decode.string))) + |> Json.Decode.andThen (\x -> Json.Decode.map x (Json.Decode.field "v" (Json.Decode.string))) + + +payTypeDecoder : Json.Decode.Decoder PayType +payTypeDecoder = + Json.Decode.oneOf + [ Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "Invoiced" -> + Json.Decode.succeed Invoiced + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + , Json.Decode.string + |> Json.Decode.andThen + (\x -> + case x of + "Paid" -> + Json.Decode.succeed Paid + unexpected -> + Json.Decode.fail <| "Unexpected variant " ++ unexpected + ) + ] + diff --git a/elm/src/TimeReporting.elm b/elm/src/TimeReporting.elm index 609ba13..eaf7b6a 100644 --- a/elm/src/TimeReporting.elm +++ b/elm/src/TimeReporting.elm @@ -3,12 +3,14 @@ module TimeReporting exposing (..) import Calendar import Clock import Csv -import Data exposing (AllocationId, PayEntryId, PayType, TimeEntryId, getProjectIdVal) +import DataUtil exposing (AllocationId, PayEntryId, TimeEntryId, getProjectIdVal) import DateTime exposing (DateTime) import Dict exposing (Dict) -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) +import Orgauth.Data exposing (UserId) +import Orgauth.UserId exposing (getUserIdVal, makeUserId) import Round as R import TDict exposing (TDict) +import TcProtocol as TP exposing (PayType) import Time exposing (Zone) import Toop import Toop.Take as TT @@ -22,7 +24,7 @@ type alias EditTimeEntry = , startdate : Int , enddate : Int , ignore : Bool - , project : Data.ProjectId + , project : TP.ProjectId , checked : Bool } @@ -222,20 +224,20 @@ teamMillisPerDay zone etes = payAmount : EditPayEntry -> Int payAmount entry = case entry.paytype of - Data.Paid -> + TP.Paid -> entry.duration - Data.Invoiced -> + TP.Invoiced -> 0 invoiceAmount : EditPayEntry -> Int invoiceAmount entry = case entry.paytype of - Data.Paid -> + TP.Paid -> 0 - Data.Invoiced -> + TP.Invoiced -> entry.duration @@ -446,7 +448,7 @@ weekTotes zone timeentries = Dict.empty -emptyUmDict : TDict UserId Int Data.User +emptyUmDict : TDict UserId Int TP.User emptyUmDict = TDict.empty getUserIdVal makeUserId @@ -472,7 +474,7 @@ timeTotes entries = emptyUserTimeDict -csvToEditTimeEntries : Time.Zone -> UserId -> Data.ProjectId -> Csv.Csv -> Result (List String) (List EditTimeEntry) +csvToEditTimeEntries : Time.Zone -> UserId -> TP.ProjectId -> Csv.Csv -> Result (List String) (List EditTimeEntry) csvToEditTimeEntries zone user projectid csv = let headers = diff --git a/elm/src/TimeTotaler.elm b/elm/src/TimeTotaler.elm index 83bf0ac..aae8147 100644 --- a/elm/src/TimeTotaler.elm +++ b/elm/src/TimeTotaler.elm @@ -1,8 +1,8 @@ module TimeTotaler exposing (TTotaler, getTes, getTotes, mapTimeentry, mkTToteler, setTes) -import Data import Dict exposing (Dict(..)) -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) +import Orgauth.Data exposing (UserId) +import Orgauth.UserId exposing (getUserIdVal, makeUserId) import Set import Time import TimeReporting as TR exposing (EditAllocation, EditPayEntry, EditTimeEntry, csvToEditAllocations, csvToEditTimeEntries, eteToCsv) diff --git a/elm/src/TimeclonkInterface.elm b/elm/src/TimeclonkInterface.elm deleted file mode 100644 index f9d1057..0000000 --- a/elm/src/TimeclonkInterface.elm +++ /dev/null @@ -1,180 +0,0 @@ -module TimeclonkInterface exposing (SendMsg(..), ServerResponse(..), encodeEmail, encodeSendMsg, serverResponseDecoder, showServerResponse) - -import Data -import Json.Decode as JD -import Json.Encode as JE -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) - - -type SendMsg - = GetProjectList UserId - | GetProjectEdit Int - | SaveProjectEdit Data.SaveProjectEdit - | SaveProjectInvoice Data.SaveProjectInvoice - | GetProjectTime Int - | SaveProjectTime Data.SaveProjectTime - | GetUserTime - | GetAllUsers - - -type ServerResponse - = ServerError String - | ProjectList (List Data.ListProject) - | ProjectEdit Data.ProjectEdit - | SavedProjectEdit Data.SavedProjectEdit - | SavedProjectInvoice Data.Project - | AllUsers (List Data.User) - | ProjectTime Data.ProjectTime - | UserTime (List Data.TimeEntry) - | NotLoggedIn - | InvalidUserOrPwd - - -showServerResponse : ServerResponse -> String -showServerResponse sr = - case sr of - ServerError e -> - "ServerError: '" ++ e ++ "'" - - ProjectList _ -> - "ProjectList" - - ProjectEdit _ -> - "ProjectEdit" - - ProjectTime _ -> - "ProjectTime" - - UserTime _ -> - "UserTime" - - SavedProjectEdit _ -> - "SavedProjectEdit" - - SavedProjectInvoice _ -> - "SavedProjectInvoice" - - AllUsers _ -> - "AllMembers" - - NotLoggedIn -> - "NotLoggedIn" - - InvalidUserOrPwd -> - "InvalidUserOrPwd" - - -encodeSendMsg : SendMsg -> JE.Value -encodeSendMsg sm = - case sm of - GetProjectList uid -> - JE.object - [ ( "what", JE.string "GetProjectList" ) - , ( "data", JE.int (getUserIdVal uid) ) - ] - - GetProjectEdit pid -> - JE.object - [ ( "what", JE.string "GetProjectEdit" ) - , ( "data", JE.int pid ) - ] - - GetProjectTime pid -> - JE.object - [ ( "what", JE.string "GetProjectTime" ) - , ( "data", JE.int pid ) - ] - - GetUserTime -> - JE.object - [ ( "what", JE.string "GetUserTime" ) - ] - - SaveProjectInvoice p -> - JE.object - [ ( "what", JE.string "SaveProjectInvoice" ) - , ( "data", Data.encodeSaveProjectInvoice p ) - ] - - SaveProjectEdit p -> - JE.object - [ ( "what", JE.string "SaveProjectEdit" ) - , ( "data", Data.encodeSaveProjectEdit p ) - ] - - SaveProjectTime p -> - JE.object - [ ( "what", JE.string "SaveProjectTime" ) - , ( "data", Data.encodeSaveProjectTime p ) - ] - - GetAllUsers -> - JE.object - [ ( "what", JE.string "GetAllUsers" ) - ] - - -encodeEmail : String -> JE.Value -encodeEmail email = - JE.object - [ ( "email", JE.string email ) - ] - - -serverResponseDecoder : JD.Decoder ServerResponse -serverResponseDecoder = - JD.at [ "what" ] - JD.string - |> JD.andThen - (\what -> - case what of - "server error" -> - JD.map ServerError (JD.at [ "content" ] JD.string) - - "projectlist" -> - JD.map ProjectList (JD.at [ "content" ] (JD.list Data.decodeListProject)) - - "savedprojectinvoice" -> - JD.map SavedProjectInvoice (JD.at [ "content" ] Data.decodeProject) - - "savedprojectedit" -> - JD.map SavedProjectEdit (JD.at [ "content" ] Data.decodeSavedProjectEdit) - - "projectedit" -> - JD.map ProjectEdit (JD.at [ "content" ] Data.decodeProjectEdit) - - "projecttime" -> - JD.map ProjectTime (JD.at [ "content" ] Data.decodeProjectTime) - - "allusers" -> - JD.map AllUsers (JD.at [ "content" ] (JD.list Data.decodeUser)) - - "usertime" -> - JD.map UserTime (JD.at [ "content" ] (JD.list Data.decodeTimeEntry)) - - "not logged in" -> - JD.succeed NotLoggedIn - - "invalid user or pwd" -> - JD.succeed InvalidUserOrPwd - - "saveprojectedit_denied" -> - JD.succeed - (ServerError "'save project' failed: insufficient user privileges") - - "projectedit_denied" -> - JD.succeed - (ServerError "'get project' failed: insufficient user privileges") - - "savedprojectinvoice_denied" -> - JD.succeed - (ServerError "'save project invoice' failed: insufficient user privileges") - - "projecttime_denied" -> - JD.succeed - (ServerError "'get project time' failed: insufficient user privileges") - - wat -> - JD.succeed - (ServerError ("invalid 'what' from server: " ++ wat)) - ) diff --git a/elm/src/UserSettings.elm b/elm/src/UserSettings.elm index f6f5666..cb3f136 100644 --- a/elm/src/UserSettings.elm +++ b/elm/src/UserSettings.elm @@ -1,7 +1,8 @@ module UserSettings exposing (..) +-- import Data + import Common exposing (buttonStyle) -import Data import Element as E exposing (Element) import Element.Background as EBk import Element.Border as EBd diff --git a/elm/src/UserTime.elm b/elm/src/UserTime.elm index 576842b..5107f04 100644 --- a/elm/src/UserTime.elm +++ b/elm/src/UserTime.elm @@ -2,29 +2,23 @@ module UserTime exposing (..) import Calendar import Common -import Csv -import Data +import DataUtil import Dict exposing (Dict) import Element as E exposing (Element) import Element.Background as EBk -import Element.Border as EBd -import Element.Events as EE import Element.Font as EF import Element.Input as EI -import Orgauth.Data as OD exposing (UserId, getUserIdVal, makeUserId) +import Orgauth.UserId exposing (getUserIdVal) import Paginator as P -import Round as R import Set -import TDict exposing (TDict) import TSet exposing (TSet) import TangoColors as TC import TcCommon as TC +import TcProtocol as TP import Time -import TimeReporting as TR exposing (EditAllocation, EditPayEntry, EditTimeEntry, csvToEditAllocations, csvToEditTimeEntries, eteToCsv, millisAsHours) -import TimeTotaler exposing (TTotaler, getTes, getTotes, mapTimeentry, mkTToteler, setTes) -import Toop +import TimeReporting as TR exposing (EditTimeEntry, eteToCsv, millisAsHours) +import TimeTotaler exposing (TTotaler, getTes, getTotes, mkTToteler) import Util -import WindowKeys as WK type Msg @@ -39,7 +33,7 @@ type Msg type alias Model = - { projects : Dict Int Data.ListProject + { projects : Dict Int TP.ListProject , timeentries : TTotaler , initialtimeentries : Dict Int EditTimeEntry , tepaginator : P.Model Msg @@ -59,14 +53,14 @@ headerStyle = [ EF.bold ] -emptyTimeEntryIdSet : TSet Data.TimeEntryId Int +emptyTimeEntryIdSet : TSet DataUtil.TimeEntryId Int emptyTimeEntryIdSet = - TSet.empty Data.getTimeEntryIdVal Data.makeTimeEntryId + TSet.empty DataUtil.getTimeEntryIdVal DataUtil.makeTimeEntryId -toEditTimeEntry : Data.TimeEntry -> EditTimeEntry +toEditTimeEntry : TP.TimeEntry -> EditTimeEntry toEditTimeEntry te = - { id = Just te.id + { id = Just (DataUtil.makeTimeEntryId te.id) , user = te.user , description = te.description , startdate = te.startdate @@ -77,14 +71,14 @@ toEditTimeEntry te = } -toEteDict : List Data.TimeEntry -> Dict Int EditTimeEntry +toEteDict : List TP.TimeEntry -> Dict Int EditTimeEntry toEteDict te = te |> List.map (toEditTimeEntry >> (\ete -> ( ete.startdate, ete ))) |> Dict.fromList -init : Time.Zone -> Data.LoginData -> List Data.TimeEntry -> Dict Int Data.ListProject -> Int -> Model +init : Time.Zone -> DataUtil.LoginData -> List TP.TimeEntry -> Dict Int TP.ListProject -> Int -> Model init zone ld timeentries projects pageincrement = let ietes = @@ -108,7 +102,7 @@ setPageIncrement pageincrement model = } -view : Data.LoginData -> Util.Size -> Time.Zone -> Model -> Element Msg +view : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> Element Msg view ld size zone model = let maxwidth = @@ -149,7 +143,7 @@ dateTimeWidth = 200 -clonkview : Data.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) +clonkview : DataUtil.LoginData -> Util.Size -> Time.Zone -> Model -> List (Element Msg) clonkview ld size zone model = let ttotes = @@ -179,7 +173,7 @@ clonkview ld size zone model = [ E.text <| let pid = - Data.getProjectIdVal te.project + DataUtil.getProjectIdVal te.project in Dict.get pid model.projects |> Maybe.map .name @@ -299,7 +293,7 @@ clonkview ld size zone model = ] -update : Msg -> Model -> Data.LoginData -> Time.Zone -> ( Model, Command ) +update : Msg -> Model -> DataUtil.LoginData -> Time.Zone -> ( Model, Command ) update msg model ld zone = case msg of SettingsPress -> @@ -310,7 +304,7 @@ update msg model ld zone = , SaveCsv ("timeclonk-" ++ ld.name ++ ".csv") (eteToCsv zone (Dict.map (\_ v -> v.name) model.projects) - (Dict.fromList [ ( OD.getUserIdVal ld.userid, ld.name ) ]) + (Dict.fromList [ ( getUserIdVal ld.userid, ld.name ) ]) (getTes model.timeentries |> Dict.values) ) ) diff --git a/orgauth b/orgauth index 927e8c7..82e4071 160000 --- a/orgauth +++ b/orgauth @@ -1 +1 @@ -Subproject commit 927e8c775785e34307d9b6cfdf00416d6e6a3f49 +Subproject commit 82e40712dbc61324e07faa1488f72d0e7be3b4b4 diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml new file mode 100644 index 0000000..96e6f03 --- /dev/null +++ b/protocol/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "protocol" +version = "0.1.0" +edition = "2024" + +[dependencies] +elm_rs = "0.2.3" +serde = "1.0.124" +serde_derive = "1.0.124" +serde_json = "1.0.64" +orgauth = { path = "../orgauth/rustlib" } diff --git a/server/src/data.rs b/protocol/src/data.rs similarity index 58% rename from server/src/data.rs rename to protocol/src/data.rs index 9f659f6..93927f9 100644 --- a/server/src/data.rs +++ b/protocol/src/data.rs @@ -1,13 +1,51 @@ +use elm_rs::{Elm, ElmDecode, ElmEncode}; +use orgauth::data::UserId; use serde_derive::{Deserialize, Serialize}; // use std::collections::HashMap; use std::fmt; +use std::fmt::Display; use std::str::FromStr; // ------------------------------------------------- +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub enum ProjectId { + Pid(i64), +} + +impl ProjectId { + pub fn to_i64(&self) -> &i64 { + match self { + ProjectId::Pid(id) => id, + } + } +} + +impl Into for ProjectId { + fn into(self) -> i64 { + match self { + ProjectId::Pid(id) => id, + } + } +} + +impl From for ProjectId { + fn from(a: i64) -> Self { + ProjectId::Pid(a) + } +} + +impl Display for ProjectId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ProjectId::Pid(id) => write!(f, "{}", id), + } + } +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct UserInviteProject { - pub id: i64, + pub id: ProjectId, pub role: Role, } @@ -16,31 +54,31 @@ pub struct UserInviteData { pub projects: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, Serialize, Deserialize, Debug, Clone)] pub struct ListProject { - pub id: i64, + pub id: ProjectId, pub name: String, pub role: Role, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct ExtraField { pub n: String, pub v: String, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Deserialize, Debug, Clone)] pub struct SaveProjectInvoice { - pub id: i64, + pub id: ProjectId, pub invoice_seq: i64, pub extra_fields: Vec, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Deserialize, Debug, Clone)] pub struct SaveProject { - pub id: Option, + pub id: Option, pub name: String, - pub description: Option, + pub description: String, pub due_days: Option, pub extra_fields: Vec, pub invoice_id_template: String, @@ -49,11 +87,11 @@ pub struct SaveProject { pub payee: String, pub generic_task: String, pub public: bool, - pub rate: Option, + pub rate: Option, pub currency: Option, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub enum Role { Member, Admin, @@ -79,14 +117,14 @@ impl FromStr for Role { } } -#[derive(Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Deserialize, Debug, Clone)] pub struct SaveProjectMember { - pub id: i64, + pub id: UserId, pub delete: bool, pub role: Role, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Deserialize, Debug, Clone)] pub struct SaveProjectEdit { pub project: SaveProject, pub members: Vec, @@ -94,19 +132,19 @@ pub struct SaveProjectEdit { #[derive(Serialize, Debug, Clone)] pub struct SavedProject { - pub id: i64, + pub id: ProjectId, pub changeddate: i64, } -#[derive(Serialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Deserialize, Serialize, Debug, Clone)] pub struct SavedProjectEdit { pub project: Project, pub members: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct Project { - pub id: i64, + pub id: ProjectId, pub name: String, pub description: String, pub due_days: Option, @@ -123,53 +161,53 @@ pub struct Project { pub changeddate: i64, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct ProjectMember { - pub id: i64, + pub id: UserId, pub name: String, pub role: Role, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, Serialize, Deserialize, Debug, Clone)] pub struct User { - pub id: i64, + pub id: UserId, pub name: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, Serialize, Deserialize, Debug, Clone)] pub struct ProjectEdit { pub project: Project, pub members: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, ElmDecode, Serialize, Deserialize, Debug, Clone)] pub struct TimeEntry { pub id: i64, - pub project: i64, - pub user: i64, + pub project: ProjectId, + pub user: UserId, pub description: String, pub startdate: i64, pub enddate: i64, pub ignore: bool, pub createdate: i64, pub changeddate: i64, - pub creator: i64, + pub creator: UserId, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct SaveTimeEntry { pub id: Option, - pub project: i64, - pub user: i64, + pub project: ProjectId, + pub user: UserId, pub description: String, pub startdate: i64, pub enddate: i64, pub ignore: bool, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct SaveProjectTime { - pub project: i64, + pub project: ProjectId, pub savetimeentries: Vec, pub deletetimeentries: Vec, pub savepayentries: Vec, @@ -178,7 +216,7 @@ pub struct SaveProjectTime { pub deleteallocations: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, ElmDecode, Serialize, Deserialize, Debug, Clone)] pub struct ProjectTime { pub project: Project, pub members: Vec, @@ -187,59 +225,59 @@ pub struct ProjectTime { pub allocations: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub enum PayType { Invoiced, Paid, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct PayEntry { pub id: i64, - pub project: i64, - pub user: i64, + pub project: ProjectId, + pub user: UserId, pub duration: i64, pub paytype: PayType, pub paymentdate: i64, pub description: String, pub createdate: i64, pub changeddate: i64, - pub creator: i64, + pub creator: UserId, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct SavePayEntry { pub id: Option, - pub project: i64, - pub user: i64, + pub project: ProjectId, + pub user: UserId, pub duration: i64, pub paytype: PayType, pub paymentdate: i64, pub description: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmDecode, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct Allocation { pub id: i64, - pub project: i64, + pub project: ProjectId, pub duration: i64, pub allocationdate: i64, pub description: String, pub createdate: i64, pub changeddate: i64, - pub creator: i64, + pub creator: UserId, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct SaveAllocation { pub id: Option, - pub project: i64, + pub project: ProjectId, pub duration: i64, pub allocationdate: i64, pub description: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct PrintInvoice { pub id: String, pub payer: String, @@ -251,7 +289,7 @@ pub struct PrintInvoice { pub currency: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Elm, ElmEncode, Serialize, Deserialize, Debug, Clone)] pub struct InvoiceItem { pub description: String, pub duration: f64, diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs new file mode 100644 index 0000000..53b3e27 --- /dev/null +++ b/protocol/src/lib.rs @@ -0,0 +1,4 @@ +extern crate serde_derive; + +pub mod data; +pub mod messages; diff --git a/protocol/src/messages.rs b/protocol/src/messages.rs new file mode 100644 index 0000000..6bd7b84 --- /dev/null +++ b/protocol/src/messages.rs @@ -0,0 +1,71 @@ +use elm_rs::{Elm, ElmDecode, ElmEncode}; +use serde_derive::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::data::{ + ListProject, Project, ProjectEdit, ProjectId, ProjectTime, SaveProjectEdit, SaveProjectInvoice, + SaveProjectTime, SavedProjectEdit, TimeEntry, User, +}; + +#[derive(Serialize, Deserialize)] +pub struct ServerResponse { + pub what: String, + pub content: Value, +} + +#[derive(Serialize, ElmDecode, Elm)] +pub enum TcResponseX { + TrProjectEdit(ProjectEdit), + TrProjectEditDenied, + TrSavedProjectEdit(SavedProjectEdit), + TrSavedProjectEditDenied, + TrSavedProjectInvoice(Project), + TrSavedProjectInvoiceDenied, + TrProjectTime(ProjectTime), + TrProjectTimeDenied, + TrProjectList(Vec), + TrUserTime(Vec), + TrAllUsers(Vec), + TrError(TimeClonkError), +} + +#[derive(Serialize, ElmDecode, Elm)] +pub enum TimeClonkError { + TeNotLoggedIn, + TeInvalidLogin, + TeOther(String), +} + +#[derive(Elm, ElmEncode, Deserialize, Debug)] +pub enum TcMessageX { + TmGetProjectList, + TmSaveProjectEdit(SaveProjectEdit), + TmGetProjectEdit(ProjectId), + TmSaveProjectInvoice(SaveProjectInvoice), + TmGetProjectTime(ProjectId), + TmSaveProjectTime(SaveProjectTime), + TmGetUserTime, + TmGetAllUsers, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct UserMessage { + pub what: String, + pub data: Option, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct PublicMessage { + pub what: String, + pub data: Option, +} + +#[derive(Elm, ElmEncode, Deserialize, Debug)] +pub enum PublicMessageX { + PmGetProjectTime(ProjectId), +} + +#[derive(Serialize, ElmDecode, Elm)] +pub enum PublicResponseX { + PrProjectTime(ProjectTime), +} diff --git a/server/Cargo.toml b/server/Cargo.toml index 106e35e..fecb1c4 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -8,8 +8,9 @@ edition = "2018" [dependencies] orgauth = { path = "../orgauth/rustlib" } +protocol = { path = "../protocol" } env_logger = "0.5.13" -rusqlite = "0.29.0" +rusqlite = { version = "0.37.0", features = ["bundled"] } barrel = { version = "0.7.0", features = ["sqlite3"] } log = "0.4.0" actix-web = "4.3.1" @@ -19,7 +20,7 @@ futures = "0.1.25" serde = "1.0.124" serde_derive = "1.0.124" serde_json = "1.0.64" -uuid = { version = "0.8", features = ["serde", "v4"] } +uuid = { version = "1.18", features = ["serde", "v4", "fast-rng"] } crypto-hash = "0.3.4" time = "0.2.27" rand = "0.5.0" @@ -31,3 +32,4 @@ timer = "0.2.0" chrono = "0.4.26" either = "1.6.1" typst = "0.11.1" +elm_rs = "0.2.3" diff --git a/server/config.toml b/server/config.toml index 1b0100f..cf83094 100644 --- a/server/config.toml +++ b/server/config.toml @@ -15,3 +15,5 @@ invite_token_expiration_ms = 86400000 static_path = "./static/" open_registration = true non_admin_invite = true +send_emails = false +remote_registration = false diff --git a/server/src/interfaces.rs b/server/src/interfaces.rs index 1f49c47..1602299 100644 --- a/server/src/interfaces.rs +++ b/server/src/interfaces.rs @@ -1,9 +1,9 @@ use crate::config::Config; -use crate::data::{Role, SaveProjectEdit, SaveProjectInvoice, SaveProjectTime}; -use crate::messages::{PublicMessage, ServerResponse, UserMessage}; use crate::sqldata; use actix_session::Session; -use log::info; +// use log::info; +use protocol::data::Role; +use protocol::messages::{PublicMessageX, PublicResponseX, TcMessageX, TcResponseX}; use std::error::Error; pub fn login_data_for_token( @@ -30,28 +30,22 @@ pub fn login_data_for_token( pub fn timeclonk_interface_loggedin( config: &Config, - uid: i64, - msg: &UserMessage, -) -> Result> { - match msg.what.as_str() { - "GetProjectList" => { + uid: orgauth::data::UserId, + msg: &TcMessageX, +) -> Result> { + match msg { + TcMessageX::TmGetProjectList => { // user can see all their projects. let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; let projects = sqldata::project_list(&conn, uid)?; - Ok(ServerResponse { - what: "projectlist".to_string(), - content: serde_json::to_value(projects)?, - }) + Ok(TcResponseX::TrProjectList(projects)) } - "SaveProjectEdit" => { - let msgdata = Option::ok_or(msg.data.as_ref(), "malformed json data")?; - let sp: SaveProjectEdit = serde_json::from_value(msgdata.clone())?; - + TcMessageX::TmSaveProjectEdit(sp) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; let allowed = match sp.project.id { None => true, // new project - Some(pid) => match sqldata::member_role(&conn, uid, pid)? { + Some(pid) => match sqldata::member_role(&conn, uid, &pid)? { Some(Role::Admin) => true, _ => false, }, @@ -59,20 +53,12 @@ pub fn timeclonk_interface_loggedin( if allowed { let saved = sqldata::save_project_edit(&conn, uid, sp)?; - Ok(ServerResponse { - what: "savedprojectedit".to_string(), - content: serde_json::to_value(saved)?, - }) + Ok(TcResponseX::TrSavedProjectEdit(saved)) } else { - Ok(ServerResponse { - what: "saveprojectedit_denied".to_string(), - content: serde_json::Value::Null, - }) + Ok(TcResponseX::TrSavedProjectEditDenied) } } - "GetProjectEdit" => { - let msgdata = Option::ok_or(msg.data.as_ref(), "malformed json data")?; - let pid: i64 = serde_json::from_value(msgdata.clone())?; + TcMessageX::TmGetProjectEdit(pid) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; let allowed = match sqldata::member_role(&conn, uid, pid)? { Some(_) => true, // any role is ok @@ -81,23 +67,14 @@ pub fn timeclonk_interface_loggedin( if allowed { let project = sqldata::read_project_edit(&conn, pid)?; - Ok(ServerResponse { - what: "projectedit".to_string(), - content: serde_json::to_value(project)?, - }) + Ok(TcResponseX::TrProjectEdit(project)) } else { - Ok(ServerResponse { - what: "projectedit_denied".to_string(), - content: serde_json::Value::Null, - }) + Ok(TcResponseX::TrProjectEditDenied) } } - "SaveProjectInvoice" => { - let msgdata = Option::ok_or(msg.data.as_ref(), "malformed json data")?; - let sp: SaveProjectInvoice = serde_json::from_value(msgdata.clone())?; - + TcMessageX::TmSaveProjectInvoice(sp) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; - let allowed = match sqldata::member_role(&conn, uid, sp.id)? { + let allowed = match sqldata::member_role(&conn, uid, &sp.id)? { Some(Role::Admin) => true, Some(Role::Member) => true, _ => false, @@ -105,20 +82,12 @@ pub fn timeclonk_interface_loggedin( if allowed { let saved = sqldata::save_project_invoice(&conn, sp)?; - Ok(ServerResponse { - what: "savedprojectinvoice".to_string(), - content: serde_json::to_value(saved)?, - }) + Ok(TcResponseX::TrSavedProjectInvoice(saved)) } else { - Ok(ServerResponse { - what: "savedprojectinvoice_denied".to_string(), - content: serde_json::Value::Null, - }) + Ok(TcResponseX::TrSavedProjectInvoiceDenied) } } - "GetProjectTime" => { - let msgdata = Option::ok_or(msg.data.as_ref(), "malformed json data")?; - let pid: i64 = serde_json::from_value(msgdata.clone())?; + TcMessageX::TmGetProjectTime(pid) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; let allowed = match sqldata::member_role(&conn, uid, pid)? { Some(_) => true, // any role is ok @@ -128,24 +97,15 @@ pub fn timeclonk_interface_loggedin( if allowed { let project = sqldata::read_project_time(&conn, pid)?; - Ok(ServerResponse { - what: "projecttime".to_string(), - content: serde_json::to_value(project)?, - }) + Ok(TcResponseX::TrProjectTime(project)) } else { - Ok(ServerResponse { - what: "projecttime_denied".to_string(), - content: serde_json::Value::Null, - }) + Ok(TcResponseX::TrProjectTimeDenied) } } - "SaveProjectTime" => { - // TODO: - let msgdata = Option::ok_or(msg.data.as_ref(), "malformed json data")?; - let spt: SaveProjectTime = serde_json::from_value(msgdata.clone())?; + TcMessageX::TmSaveProjectTime(spt) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; - let allowed = match sqldata::member_role(&conn, uid, spt.project)? { + let allowed = match sqldata::member_role(&conn, uid, &spt.project)? { Some(Role::Admin) => true, Some(Role::Member) => true, _ => false, @@ -154,70 +114,43 @@ pub fn timeclonk_interface_loggedin( if allowed { let bak = sqldata::save_project_time(&conn, uid, spt)?; - Ok(ServerResponse { - what: "projecttime".to_string(), - content: serde_json::to_value(bak)?, - }) + Ok(TcResponseX::TrProjectTime(bak)) } else { - Ok(ServerResponse { - what: "projecttime_denied".to_string(), - content: serde_json::Value::Null, - }) + Ok(TcResponseX::TrProjectTimeDenied) } } - "GetUserTime" => { + TcMessageX::TmGetUserTime => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; let time = sqldata::user_time(&conn, uid)?; - Ok(ServerResponse { - what: "usertime".to_string(), - content: serde_json::to_value(time)?, - }) + Ok(TcResponseX::TrUserTime(time)) } - "GetAllUsers" => { + TcMessageX::TmGetAllUsers => { // all users can see all users! let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; let members = sqldata::user_list(&conn)?; - Ok(ServerResponse { - what: "allusers".to_string(), - content: serde_json::to_value(members)?, - }) + Ok(TcResponseX::TrAllUsers(members)) } - wat => Err(Box::new(simple_error::SimpleError::new(format!( - "invalid 'what' code:'{}'", - wat - )))), } } // public json msgs don't require login. pub fn public_interface( config: &Config, - msg: PublicMessage, -) -> Result> { - info!("process_public_json, what={}", msg.what.as_str()); - match msg.what.as_str() { - "GetProjectTime" => { - let msgdata = Option::ok_or(msg.data.as_ref(), "malformed json data")?; - let pid: i64 = serde_json::from_value(msgdata.clone())?; + msg: PublicMessageX, +) -> Result> { + match msg { + PublicMessageX::PmGetProjectTime(pid) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; - let project = sqldata::read_project_time(&conn, pid)?; + let project = sqldata::read_project_time(&conn, &pid)?; if project.project.public { - Ok(ServerResponse { - what: "projecttime".to_string(), - content: serde_json::to_value(project)?, - }) + Ok(PublicResponseX::PrProjectTime(project)) } else { - Ok(ServerResponse { - what: "projecttime-denied".to_string(), - content: serde_json::Value::Null, - }) + Err(Box::new(simple_error::SimpleError::new(format!( + "can't access project!" + )))) } } - wat => Err(Box::new(simple_error::SimpleError::new(format!( - "invalid 'what' code:'{}'", - wat - )))), } } diff --git a/server/src/invoice.rs b/server/src/invoice.rs index 5893850..819bee4 100644 --- a/server/src/invoice.rs +++ b/server/src/invoice.rs @@ -1,5 +1,4 @@ use crate::config::Config; -use crate::data::{InvoiceItem, PrintInvoice}; use crate::sqldata; use actix_files::NamedFile; use actix_session::Session; @@ -7,6 +6,7 @@ use actix_web::{ error::{ErrorInternalServerError, ErrorUnauthorized}, web, HttpRequest, }; +use protocol::data::{InvoiceItem, PrintInvoice}; use std::path::PathBuf; use std::process::Command; use uuid::Uuid; diff --git a/server/src/main.rs b/server/src/main.rs index 32339e5..9a7d978 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,8 +1,7 @@ mod config; -mod data; +use protocol::messages::{PublicMessageX, ServerResponse, TcMessageX, TcResponseX, TimeClonkError}; mod interfaces; mod invoice; -mod messages; mod migrations; mod sqldata; use actix_session::{ @@ -15,9 +14,11 @@ use actix_web::{ use clap::Arg; use config::Config; use log::{error, info}; -use messages::{PublicMessage, ServerResponse, UserMessage}; -use orgauth::data::WhatMessage; -use orgauth::util; +use orgauth::{data::UserResponse, util}; +use orgauth::{ + data::{AdminResponse, UserRequest}, + endpoints::ActixTokener, +}; use serde_json; use std::env; use std::error::Error; @@ -82,7 +83,7 @@ async fn mainpage(session: Session, data: web::Data, req: HttpRequest) - async fn public( data: web::Data, - item: web::Json, + item: web::Json, req: HttpRequest, ) -> HttpResponse { info!( @@ -107,29 +108,31 @@ async fn public( async fn user( session: Session, data: web::Data, - item: web::Json, + item: web::Json, req: HttpRequest, ) -> HttpResponse { - info!( - "user msg: {} \n connection_info: {:?}", - &item.what, - req.connection_info() - ); + info!("user msg: \n connection_info: {:?}", req.connection_info()); let mut cb = sqldata::timeclonk_callbacks(); - match orgauth::endpoints::user_interface( - &session, - &data.orgauth_config, - &mut cb, - item.into_inner(), - ) { + match async { + let conn = sqldata::connection_open(data.orgauth_config.db.as_path())?; + + orgauth::endpoints::user_interface( + &conn, + &mut ActixTokener { session: &session }, + &data.orgauth_config, + &mut cb, + None, + item.into_inner(), + ) + .await + } + .await + { Ok(sr) => HttpResponse::Ok().json(sr), Err(e) => { error!("'user' err: {:?}", e); - let se = orgauth::data::WhatMessage { - what: "server error".to_string(), - data: Some(serde_json::Value::String(e.to_string())), - }; + let se = UserResponse::UrpServerError(e.to_string()); HttpResponse::Ok().json(se) } } @@ -138,17 +141,17 @@ async fn user( async fn admin( session: Session, data: web::Data, - item: web::Json, + item: web::Json, req: HttpRequest, ) -> HttpResponse { info!( - "admin msg: {} \n connection_info: {:?}", - &item.what, + "admin msg: \n connection_info: {:?}", + // &item.what, req.connection_info() ); let mut cb = sqldata::timeclonk_callbacks(); match orgauth::endpoints::admin_interface_check( - &session, + &mut ActixTokener { session: &session }, &data.orgauth_config, &mut cb, item.into_inner(), @@ -156,10 +159,7 @@ async fn admin( Ok(sr) => HttpResponse::Ok().json(sr), Err(e) => { error!("'user' err: {:?}", e); - let se = orgauth::data::WhatMessage { - what: "server error".to_string(), - data: Some(serde_json::Value::String(e.to_string())), - }; + let se = AdminResponse::ArpServerError(e.to_string()); HttpResponse::Ok().json(se) } } @@ -168,18 +168,14 @@ async fn admin( async fn private( session: Session, data: web::Data, - item: web::Json, + item: web::Json, _req: HttpRequest, ) -> HttpResponse { match timeclonk_interface_check(&session, &data, item.into_inner()) { Ok(sr) => HttpResponse::Ok().json(sr), Err(e) => { error!("'private' err: {:?}", e); - let se = ServerResponse { - what: "server error".to_string(), - content: serde_json::Value::String(e.to_string()), - }; - HttpResponse::Ok().json(se) + HttpResponse::Ok().json(TcResponseX::TrError(TimeClonkError::TeOther(e.to_string()))) } } } @@ -187,13 +183,10 @@ async fn private( fn timeclonk_interface_check( session: &Session, config: &Config, - msg: UserMessage, -) -> Result> { + msg: TcMessageX, +) -> Result> { match session.get::("token")? { - None => Ok(ServerResponse { - what: "not logged in".to_string(), - content: serde_json::Value::Null, - }), + None => Ok(TcResponseX::TrError(TimeClonkError::TeNotLoggedIn)), Some(token) => { let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; match orgauth::dbfun::read_user_by_token_api( @@ -205,14 +198,11 @@ fn timeclonk_interface_check( Err(e) => { info!("read_user_by_token_api error: {:?}", e); - Ok(ServerResponse { - what: "invalid user or pwd".to_string(), - content: serde_json::Value::Null, - }) + Ok(TcResponseX::TrError(TimeClonkError::TeInvalidLogin)) } Ok(userdata) => { // finally! processing messages as logged in user. - interfaces::timeclonk_interface_loggedin(&config, userdata.id, &msg) + interfaces::timeclonk_interface_loggedin(&config, userdata.id.into(), &msg) } } } @@ -241,6 +231,8 @@ fn defcon() -> Config { invite_token_expiration_ms: 1 * 24 * 60 * 60 * 1000, // 1 day in milliseconds open_registration: false, non_admin_invite: false, + remote_registration: false, + send_emails: false, }; Config { ip: "127.0.0.1".to_string(), @@ -256,6 +248,7 @@ pub fn load_config(filename: &str) -> Result> { } fn main() { + env_logger::init(); match err_main() { Err(e) => error!("error: {:?}", e), Ok(_) => (), @@ -343,8 +336,6 @@ async fn err_main() -> Result<(), Box> { } None => { // normal server ops - env_logger::init(); - info!("server init!"); if config.static_path == None { @@ -398,14 +389,27 @@ async fn err_main() -> Result<(), Box> { let mut cb = sqldata::timeclonk_callbacks(); let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?; - // make new registration i + // make new registration let rd = orgauth::data::RegistrationData { uid: username.to_string(), pwd: pwd.trim().to_string(), email: "".to_string(), + remote_url: "".to_string(), }; - orgauth::dbfun::new_user(&conn, &rd, None, None, true, None, &mut cb.on_new_user)?; + orgauth::dbfun::new_user( + &conn, + &rd, + None, + None, + true, + None, + None, + None, + None, + None, + &mut cb.on_new_user, + )?; println!("admin user created: {}", username); return Ok(()); diff --git a/server/src/messages.rs b/server/src/messages.rs deleted file mode 100644 index 69d2e21..0000000 --- a/server/src/messages.rs +++ /dev/null @@ -1,20 +0,0 @@ -use serde_derive::{Deserialize, Serialize}; -use serde_json::Value; - -#[derive(Serialize, Deserialize)] -pub struct ServerResponse { - pub what: String, - pub content: Value, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct UserMessage { - pub what: String, - pub data: Option, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct PublicMessage { - pub what: String, - pub data: Option, -} diff --git a/server/src/migrations.rs b/server/src/migrations.rs index 61c9bdc..0eedd0b 100644 --- a/server/src/migrations.rs +++ b/server/src/migrations.rs @@ -1039,3 +1039,9 @@ pub fn udpate12(dbfile: &Path) -> Result<(), orgauth::error::Error> { Ok(()) } + +pub fn udpate13(dbfile: &Path) -> Result<(), orgauth::error::Error> { + om::udpate8(dbfile)?; + + Ok(()) +} diff --git a/server/src/sqldata.rs b/server/src/sqldata.rs index df1de0c..750c948 100644 --- a/server/src/sqldata.rs +++ b/server/src/sqldata.rs @@ -1,14 +1,15 @@ -use crate::data::{ - Allocation, ListProject, PayEntry, PayType, Project, ProjectEdit, ProjectMember, ProjectTime, - Role, SaveAllocation, SavePayEntry, SaveProject, SaveProjectEdit, SaveProjectInvoice, - SaveProjectTime, SaveTimeEntry, SavedProject, SavedProjectEdit, TimeEntry, User, UserInviteData, -}; use crate::migrations as tm; use barrel::backend::Sqlite; use log::info; -use orgauth::data::RegistrationData; +use orgauth::data::{RegistrationData, UserId}; use orgauth::endpoints::Callbacks; use orgauth::util::now; +use protocol::data::{ + Allocation, ListProject, PayEntry, PayType, Project, ProjectEdit, ProjectId, ProjectMember, + ProjectTime, Role, SaveAllocation, SavePayEntry, SaveProject, SaveProjectEdit, + SaveProjectInvoice, SaveProjectTime, SaveTimeEntry, SavedProject, SavedProjectEdit, TimeEntry, + User, UserInviteData, +}; use rusqlite::{params, Connection}; use std::path::Path; use std::str::FromStr; @@ -25,23 +26,28 @@ pub fn timeclonk_callbacks() -> Callbacks { pub fn on_new_user( conn: &Connection, _rd: &RegistrationData, - data: Option, - creator: Option, - uid: i64, + extra_login_data: Option, + _remote_data: Option, + creator: Option, + uid: UserId, ) -> Result<(), orgauth::error::Error> { - match data { + match extra_login_data { Some(d) => { let invitedata: UserInviteData = serde_json::from_str(d.as_str())?; match creator { Some(cuid) => { for p in invitedata.projects { - match member_role(conn, cuid, p.id)? { + match member_role(conn, cuid.into(), &p.id)? { Some(Role::Admin) => { conn.execute( "insert into projectmember (project, user, role) values (?1, ?2, ?3) on conflict (project, user) do update set role = ?3", - params![p.id, uid, p.role.to_string().as_str()], + params![ + Into::::into(p.id), + Into::::into(uid), + p.role.to_string().as_str() + ], )?; } Some(_) => (), @@ -58,14 +64,14 @@ pub fn on_new_user( } } -pub fn on_delete_user(_conn: &Connection, _uid: i64) -> Result { +pub fn on_delete_user(_conn: &Connection, _uid: UserId) -> Result { Ok(true) } // callback to pass to orgauth pub fn extra_login_data_callback( _conn: &Connection, - _uid: i64, + _uid: UserId, ) -> Result, orgauth::error::Error> { Ok(None) } @@ -199,6 +205,11 @@ pub fn dbinit( tm::udpate12(&dbfile)?; set_single_value(&conn, "migration_level", "12")?; } + if nlevel < 13 { + info!("udpate13"); + tm::udpate13(&dbfile)?; + set_single_value(&conn, "migration_level", "13")?; + } info!("db up to date."); @@ -213,12 +224,12 @@ pub fn dbinit( pub fn member_role( conn: &Connection, - uid: i64, - pid: i64, + uid: UserId, + pid: &ProjectId, ) -> Result, orgauth::error::Error> { match conn.query_row( "select role from projectmember where project = ?1 and user = ?2", - params![pid, uid], + params![pid.to_i64(), uid.to_i64()], |row| Ok(row.get::(0)?), ) { Ok(v) => match Role::from_str(v.as_str()) { @@ -232,7 +243,7 @@ pub fn member_role( pub fn project_list( conn: &Connection, - uid: i64, + uid: UserId, ) -> Result, orgauth::error::Error> { // projects ordered by last clonk. let mut pstmt = conn.prepare( @@ -246,10 +257,10 @@ pub fn project_list( ", )?; let mut r: Vec = pstmt - .query_map(params![uid], |row| { + .query_map(params![uid.to_i64()], |row| { let role: String = row.get(2)?; Ok(ListProject { - id: row.get(0)?, + id: ProjectId::Pid(row.get(0)?), name: row.get(1)?, role: match Role::from_str(role.as_str()) { Ok(r) => r, @@ -270,10 +281,10 @@ pub fn project_list( ", )?; let mut rempty: Vec = pstmt - .query_map(params![uid], |row| { + .query_map(params![uid.to_i64()], |row| { let role: String = row.get(2)?; Ok(ListProject { - id: row.get(0)?, + id: ProjectId::Pid(row.get(0)?), name: row.get(1)?, role: match Role::from_str(role.as_str()) { Ok(r) => r, @@ -291,30 +302,31 @@ pub fn project_list( pub fn save_project_edit( conn: &Connection, - user: i64, - project_edit: SaveProjectEdit, + user: UserId, + project_edit: &SaveProjectEdit, ) -> Result { - let sp = save_project(conn, user, project_edit.project)?; + let sp = save_project(conn, user, &project_edit.project)?; - for m in project_edit.members { + for m in &project_edit.members { if m.delete { conn.execute( "delete from projectmember where user = ?1 and project = ?2", - params![sp.id, m.id], + params![m.id.to_i64(), sp.id.to_i64()], + // wasn't this WRONG?? TODO: test. )?; } else { conn.execute( "insert into projectmember (project, user, role) values (?1, ?2, ?3) on conflict (project, user) do update set role = ?3", - params![sp.id, m.id, m.role.to_string().as_str()], + params![sp.id.to_i64(), m.id.to_i64(), m.role.to_string().as_str()], )?; } } - let proj = read_project(conn, sp.id)?; - let mems = member_list(conn, sp.id)?; + let proj = read_project(conn, &sp.id)?; + let mems = member_list(conn, &sp.id)?; Ok(SavedProjectEdit { project: proj, @@ -324,7 +336,7 @@ pub fn save_project_edit( pub fn save_project_invoice( conn: &Connection, - project: SaveProjectInvoice, + project: &SaveProjectInvoice, ) -> Result { let now = orgauth::util::now()?; conn.execute( @@ -334,21 +346,21 @@ pub fn save_project_invoice( where id = ?4", params![ project.invoice_seq, - serde_json::to_value(project.extra_fields)?.to_string(), + serde_json::to_value(project.extra_fields.clone())?.to_string(), now, - project.id + project.id.to_i64() ], )?; - let proj = read_project(conn, project.id)?; + let proj = read_project(conn, &project.id)?; Ok(proj) } pub fn save_project( conn: &Connection, - user: i64, - project: SaveProject, + user: UserId, + project: &SaveProject, ) -> Result { let now = now()?; @@ -373,7 +385,7 @@ pub fn save_project( project.name, project.description, project.due_days, - serde_json::to_value(project.extra_fields)?.to_string(), + serde_json::to_value(project.extra_fields.clone())?.to_string(), project.invoice_id_template, project.invoice_seq, project.payer, @@ -383,7 +395,7 @@ pub fn save_project( project.rate, project.currency, now, - id + id.to_i64() ], )?; SavedProject { @@ -399,7 +411,7 @@ pub fn save_project( project.name, project.description, project.due_days, - serde_json::to_value(project.extra_fields)?.to_string(), + serde_json::to_value(project.extra_fields.clone())?.to_string(), project.invoice_id_template, project.invoice_seq, project.payer, @@ -416,10 +428,10 @@ pub fn save_project( conn.execute( "insert into projectmember (project, user, role) values (?1, ?2, 'Admin')", - params![id, user], + params![id, user.to_i64()], )?; SavedProject { - id: id, + id: ProjectId::Pid(id), changeddate: now, } } @@ -427,7 +439,10 @@ pub fn save_project( Ok(proj) } -pub fn read_project(conn: &Connection, projectid: i64) -> Result { +pub fn read_project( + conn: &Connection, + projectid: &ProjectId, +) -> Result { let mut pstmt = conn.prepare( "select project.id, project.name, @@ -447,9 +462,9 @@ pub fn read_project(conn: &Connection, projectid: i64) -> Result Result Result, orgauth::error::Error> { let mut pstmt = conn.prepare( "select orgauth_user.id, orgauth_user.name, projectmember.role from orgauth_user, projectmember where @@ -485,10 +500,10 @@ pub fn member_list( projectmember.project = ?1", )?; let r = pstmt - .query_map(params![projectid], |row| { + .query_map(params![projectid.to_i64()], |row| { match Role::from_str(row.get::(2)?.as_str()) { Ok(role) => Ok(ProjectMember { - id: row.get(0)?, + id: UserId::Uid(row.get(0)?), name: row.get(1)?, role: role, }), @@ -515,7 +530,7 @@ pub fn user_list(conn: &Connection) -> Result, orgauth::error::Error> let r = pstmt .query_map(params![], |row| { Ok(User { - id: row.get(0)?, + id: UserId::Uid(row.get(0)?), name: row.get(1)?, }) })? @@ -529,7 +544,7 @@ pub fn user_list(conn: &Connection) -> Result, orgauth::error::Error> pub fn read_project_edit( conn: &Connection, - projectid: i64, + projectid: &ProjectId, ) -> Result { let proj = read_project(conn, projectid)?; let members = member_list(conn, projectid)?; @@ -539,7 +554,10 @@ pub fn read_project_edit( }) } -pub fn user_time(conn: &Connection, userid: i64) -> Result, orgauth::error::Error> { +pub fn user_time( + conn: &Connection, + userid: UserId, +) -> Result, orgauth::error::Error> { let mut pstmt = conn.prepare( "select te.id, te.project, te.user, te.description, te.startdate, te.enddate, te.ignore, te.createdate, te.changeddate, te.creator from timeentry te where @@ -547,18 +565,18 @@ pub fn user_time(conn: &Connection, userid: i64) -> Result, orgau )?; let r = Ok( pstmt - .query_map(params![userid], |row| { + .query_map(params![userid.to_i64()], |row| { Ok(TimeEntry { id: row.get(0)?, - project: row.get(1)?, - user: row.get(2)?, + project: ProjectId::Pid(row.get(1)?), + user: UserId::Uid(row.get(2)?), description: row.get(3)?, startdate: row.get(4)?, enddate: row.get(5)?, ignore: row.get(6)?, createdate: row.get(7)?, changeddate: row.get(8)?, - creator: row.get(9)?, + creator: UserId::Uid(row.get(9)?), }) })? .filter_map(|x| x.ok()) @@ -569,7 +587,7 @@ pub fn user_time(conn: &Connection, userid: i64) -> Result, orgau pub fn time_entries( conn: &Connection, - projectid: i64, + projectid: &ProjectId, ) -> Result, orgauth::error::Error> { let mut pstmt = conn.prepare( "select te.id, te.project, te.user, te.description, te.startdate, te.enddate, te.ignore, te.createdate, te.changeddate, te.creator @@ -578,18 +596,18 @@ pub fn time_entries( )?; let r = Ok( pstmt - .query_map(params![projectid], |row| { + .query_map(params![projectid.to_i64()], |row| { Ok(TimeEntry { id: row.get(0)?, - project: row.get(1)?, - user: row.get(2)?, + project: ProjectId::Pid(row.get(1)?), + user: UserId::Uid(row.get(2)?), description: row.get(3)?, startdate: row.get(4)?, enddate: row.get(5)?, ignore: row.get(6)?, createdate: row.get(7)?, changeddate: row.get(8)?, - creator: row.get(9)?, + creator: UserId::Uid(row.get(9)?), }) })? .filter_map(|x| x.ok()) @@ -600,7 +618,7 @@ pub fn time_entries( pub fn pay_entries( conn: &Connection, - projectid: i64, + projectid: &ProjectId, ) -> Result, orgauth::error::Error> { let mut pstmt = conn.prepare( "select pe.id, pe.project, pe.user, pe.duration, pe.type, pe.paymentdate, pe.description, pe.createdate, pe.changeddate, pe.creator @@ -609,11 +627,11 @@ pub fn pay_entries( )?; let r = Ok( pstmt - .query_map(params![projectid], |row| { + .query_map(params![projectid.to_i64()], |row| { Ok(PayEntry { id: row.get(0)?, - project: row.get(1)?, - user: row.get(2)?, + project: ProjectId::Pid(row.get(1)?), + user: UserId::Uid(row.get(2)?), duration: row.get(3)?, paytype: { let pt: i64 = row.get(4)?; @@ -627,7 +645,7 @@ pub fn pay_entries( description: row.get(6)?, createdate: row.get(7)?, changeddate: row.get(8)?, - creator: row.get(9)?, + creator: UserId::Uid(row.get(9)?), }) })? .filter_map(|x| x.ok()) @@ -638,8 +656,8 @@ pub fn pay_entries( pub fn save_pay_entry( conn: &Connection, - uid: i64, - spe: SavePayEntry, + uid: UserId, + spe: &SavePayEntry, ) -> Result { let now = now()?; let pt = match spe.paytype { @@ -658,13 +676,13 @@ pub fn save_pay_entry( , paymentdate =?6 , changeddate =?7 where id = ?8 ", - params![spe.project, spe.user, spe.description, spe.duration, pt, spe.paymentdate, now, id], + params![spe.project.to_i64(), spe.user.to_i64(), spe.description, spe.duration, pt, spe.paymentdate, now, id], )?, None => conn.execute( "insert into payentry (project, user, description, duration, type, paymentdate, createdate, changeddate, creator) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)", - params![spe.project, spe.user, spe.description, spe.duration, pt, spe.paymentdate, now, now, uid], + params![spe.project.to_i64(), spe.user.to_i64(), spe.description, spe.duration, pt, spe.paymentdate, now, now, uid.to_i64()], )?, }; let id = conn.last_insert_rowid(); @@ -674,7 +692,7 @@ pub fn save_pay_entry( // check for user membership before calling! pub fn delete_pay_entry( conn: &Connection, - _uid: i64, + _uid: UserId, peid: i64, ) -> Result<(), orgauth::error::Error> { conn.execute("delete from payentry where id = ?1", params![peid])?; @@ -683,7 +701,7 @@ pub fn delete_pay_entry( pub fn allocations( conn: &Connection, - projectid: i64, + projectid: &ProjectId, ) -> Result, orgauth::error::Error> { let mut pstmt = conn.prepare( "select a.id, a.project, a.duration, a.allocationdate, a.description, a.createdate, a.changeddate, a.creator @@ -692,16 +710,16 @@ pub fn allocations( )?; let r = Ok( pstmt - .query_map(params![projectid], |row| { + .query_map(params![projectid.to_i64()], |row| { Ok(Allocation { id: row.get(0)?, - project: row.get(1)?, + project: ProjectId::Pid(row.get(1)?), duration: row.get(2)?, allocationdate: row.get(3)?, description: row.get(4)?, createdate: row.get(5)?, changeddate: row.get(6)?, - creator: row.get(7)?, + creator: UserId::Uid(row.get(7)?), }) })? .filter_map(|x| x.ok()) @@ -712,8 +730,8 @@ pub fn allocations( pub fn save_allocation( conn: &Connection, - uid: i64, - sa: SaveAllocation, + uid: UserId, + sa: &SaveAllocation, ) -> Result { let now = now()?; match sa.id { @@ -726,13 +744,13 @@ pub fn save_allocation( , allocationdate =?4 , changeddate =?5 where id = ?6 ", - params![sa.project, sa.description, sa.duration, sa.allocationdate, now, id], + params![sa.project.to_i64(), sa.description, sa.duration, sa.allocationdate, now, id], )?, None => conn.execute( "insert into allocation (project, description, duration, allocationdate, createdate, changeddate, creator) values (?1, ?2, ?3, ?4, ?5, ?6, ?7)", - params![sa.project, sa.description, sa.duration, sa.allocationdate, now, now, uid], + params![sa.project.to_i64(), sa.description, sa.duration, sa.allocationdate, now, now, uid.to_i64()], )?, }; let id = conn.last_insert_rowid(); @@ -742,7 +760,7 @@ pub fn save_allocation( // check for user membership before calling! pub fn delete_allocation( conn: &Connection, - _uid: i64, + _uid: UserId, id: i64, ) -> Result<(), orgauth::error::Error> { conn.execute("delete from allocation where id = ?1", params![id])?; @@ -751,12 +769,12 @@ pub fn delete_allocation( pub fn is_project_member( conn: &Connection, - uid: i64, - projectid: i64, + uid: UserId, + projectid: ProjectId, ) -> Result { match conn.query_row( "select user from projectmember where user = ?1 and project = ?2", - params![uid, projectid], + params![uid.to_i64(), projectid.to_i64()], |_row| Ok(()), ) { Ok(_v) => Ok(true), @@ -767,7 +785,7 @@ pub fn is_project_member( pub fn read_project_time( conn: &Connection, - projectid: i64, + projectid: &ProjectId, ) -> Result { let proj = read_project(conn, projectid)?; let members = member_list(conn, projectid)?; @@ -785,8 +803,8 @@ pub fn read_project_time( pub fn save_time_entry( conn: &Connection, - uid: i64, - spt: SaveTimeEntry, + uid: UserId, + spt: &SaveTimeEntry, ) -> Result { let now = now()?; match spt.id { @@ -801,13 +819,13 @@ pub fn save_time_entry( ignore = ?6, changeddate = ?7 where id = ?8", - params![spt.project, spt.user, spt.description, spt.startdate, spt.enddate, spt.ignore, now, id], + params![spt.project.to_i64(), spt.user.to_i64(), spt.description, spt.startdate, spt.enddate, spt.ignore, now, id], )?, None => conn.execute( "insert into timeentry (project, user, description, startdate, enddate, ignore, createdate, changeddate, creator) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)", - params![spt.project, spt.user, spt.description, spt.startdate, spt.enddate, spt.ignore, now, now, uid], + params![spt.project.to_i64(), spt.user.to_i64(), spt.description, spt.startdate, spt.enddate, spt.ignore, now, now, uid.to_i64()], )?, }; let id = conn.last_insert_rowid(); @@ -817,7 +835,7 @@ pub fn save_time_entry( // check for user membership before calling! pub fn delete_time_entry( conn: &Connection, - _uid: i64, + _uid: UserId, teid: i64, ) -> Result<(), orgauth::error::Error> { conn.execute("delete from timeentry where id = ?1", params![teid])?; @@ -826,30 +844,30 @@ pub fn delete_time_entry( pub fn save_project_time( conn: &Connection, - uid: i64, - spt: SaveProjectTime, + uid: UserId, + spt: &SaveProjectTime, ) -> Result { // is user a member of this project? if is_project_member(conn, uid, spt.project)? { - for te in spt.savetimeentries { + for te in &spt.savetimeentries { save_time_entry(conn, uid, te)?; } - for id in spt.deletetimeentries { - delete_time_entry(conn, uid, id)?; + for id in &spt.deletetimeentries { + delete_time_entry(conn, uid, id.clone())?; } - for te in spt.savepayentries { + for te in &spt.savepayentries { save_pay_entry(conn, uid, te)?; } - for id in spt.deletepayentries { - delete_pay_entry(conn, uid, id)?; + for id in &spt.deletepayentries { + delete_pay_entry(conn, uid, id.clone())?; } - for te in spt.saveallocations { + for te in &spt.saveallocations { save_allocation(conn, uid, te)?; } - for id in spt.deleteallocations { - delete_allocation(conn, uid, id)?; + for id in &spt.deleteallocations { + delete_allocation(conn, uid, id.clone())?; } } - read_project_time(conn, spt.project) + read_project_time(conn, &spt.project) }