Compare commits
38 Commits
bugfix/ui-
...
profile-fe
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b8cd8b9f7 | ||
![]() |
3bc8dd4239 | ||
![]() |
e22b0a5fd1 | ||
![]() |
033e4f5a41 | ||
![]() |
4725d8513b | ||
![]() |
7a12c5e610 | ||
![]() |
4d3e487011 | ||
![]() |
cc00b78711 | ||
![]() |
f8f1b28ea9 | ||
![]() |
e2d4d8eba9 | ||
![]() |
7b9333a950 | ||
![]() |
5f9a3d8e68 | ||
![]() |
e228a202c8 | ||
![]() |
8f50189da1 | ||
![]() |
4613021498 | ||
![]() |
45e13b6149 | ||
![]() |
80ac081234 | ||
![]() |
19a1b8d080 | ||
![]() |
2c80026aba | ||
![]() |
117e924f66 | ||
![]() |
909416fbe3 | ||
![]() |
41998a13be | ||
![]() |
ce86b318fd | ||
![]() |
35b5865c25 | ||
![]() |
accda2f16e | ||
![]() |
7a2df1080a | ||
![]() |
a8008dee88 | ||
![]() |
3277e7ae0a | ||
![]() |
6ca547e802 | ||
![]() |
f1ec07a179 | ||
![]() |
e4c8e46a49 | ||
![]() |
5937afc9a4 | ||
![]() |
bc81fc231f | ||
![]() |
41acf38fb5 | ||
![]() |
d688cf79a4 | ||
![]() |
2e34212c93 | ||
![]() |
e0ecec7d81 | ||
![]() |
dfd90b323c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ Server/sessions/
|
||||
.vscode/settings.json
|
||||
Server/app/resources/emails/credentials.json
|
||||
Server/db/connection.yml
|
||||
Server/public/assets/groups.json
|
@@ -1,8 +1,8 @@
|
||||
# This file is machine-generated - editing it directly is not advised
|
||||
|
||||
julia_version = "1.9.0"
|
||||
julia_version = "1.9.1"
|
||||
manifest_format = "2.0"
|
||||
project_hash = "829f3e210629ef04542eb44fc4cb5acdb74d23eb"
|
||||
project_hash = "09d33216e2516631ede3cbab2af65d3f95eb0598"
|
||||
|
||||
[[deps.Adapt]]
|
||||
deps = ["LinearAlgebra", "Requires"]
|
||||
@@ -62,9 +62,9 @@ version = "1.3.1"
|
||||
|
||||
[[deps.CodecZlib]]
|
||||
deps = ["TranscodingStreams", "Zlib_jll"]
|
||||
git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83"
|
||||
git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092"
|
||||
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
|
||||
[[deps.CommonMark]]
|
||||
deps = ["Crayons", "JSON", "PrecompileTools", "URIs"]
|
||||
@@ -74,9 +74,9 @@ version = "0.8.12"
|
||||
|
||||
[[deps.Compat]]
|
||||
deps = ["UUIDs"]
|
||||
git-tree-sha1 = "4e88377ae7ebeaf29a047aa1ee40826e0b708a5d"
|
||||
git-tree-sha1 = "5ce999a19f4ca23ea484e92a1774a61b8ca4cf8e"
|
||||
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
|
||||
version = "4.7.0"
|
||||
version = "4.8.0"
|
||||
weakdeps = ["Dates", "LinearAlgebra"]
|
||||
|
||||
[deps.Compat.extensions]
|
||||
@@ -89,31 +89,36 @@ version = "1.0.2+0"
|
||||
|
||||
[[deps.ConcurrentUtilities]]
|
||||
deps = ["Serialization", "Sockets"]
|
||||
git-tree-sha1 = "96d823b94ba8d187a6d8f0826e731195a74b90e9"
|
||||
git-tree-sha1 = "5372dbbf8f0bdb8c700db5367132925c0771ef7e"
|
||||
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
|
||||
version = "2.2.0"
|
||||
version = "2.2.1"
|
||||
|
||||
[[deps.Crayons]]
|
||||
git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
|
||||
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
|
||||
version = "4.1.1"
|
||||
|
||||
[[deps.DBInterface]]
|
||||
git-tree-sha1 = "9b0dc525a052b9269ccc5f7f04d5b3639c65bca5"
|
||||
uuid = "a10d1c49-ce27-4219-8d33-6db1a4562965"
|
||||
version = "2.5.0"
|
||||
|
||||
[[deps.DataAPI]]
|
||||
git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c"
|
||||
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
|
||||
version = "1.15.0"
|
||||
|
||||
[[deps.DataFrames]]
|
||||
deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
|
||||
git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee"
|
||||
deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
|
||||
git-tree-sha1 = "04c738083f29f86e62c8afc341f0967d8717bdb8"
|
||||
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
version = "1.5.0"
|
||||
version = "1.6.1"
|
||||
|
||||
[[deps.DataStructures]]
|
||||
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
|
||||
git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0"
|
||||
git-tree-sha1 = "cf25ccb972fec4e4817764d01c82386ae94f77b4"
|
||||
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
version = "0.18.13"
|
||||
version = "0.18.14"
|
||||
|
||||
[[deps.DataValueInterfaces]]
|
||||
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
|
||||
@@ -168,9 +173,9 @@ uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4"
|
||||
version = "0.1.9"
|
||||
|
||||
[[deps.ExprTools]]
|
||||
git-tree-sha1 = "c1d06d129da9f55715c6c212866f5b1bddc5fa00"
|
||||
git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec"
|
||||
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
|
||||
[[deps.EzXML]]
|
||||
deps = ["Printf", "XML2_jll"]
|
||||
@@ -193,12 +198,6 @@ version = "0.9.20"
|
||||
[[deps.FileWatching]]
|
||||
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
|
||||
|
||||
[[deps.Formatting]]
|
||||
deps = ["Printf"]
|
||||
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
|
||||
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
|
||||
version = "0.4.2"
|
||||
|
||||
[[deps.Future]]
|
||||
deps = ["Random"]
|
||||
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"
|
||||
@@ -257,9 +256,9 @@ version = "1.3.1"
|
||||
|
||||
[[deps.HTTP]]
|
||||
deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"]
|
||||
git-tree-sha1 = "2613d054b0e18a3dea99ca1594e9a3960e025da4"
|
||||
git-tree-sha1 = "cb56ccdd481c0dd7f975ad2b3b62d9eda088f7e2"
|
||||
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
version = "1.9.7"
|
||||
version = "1.9.14"
|
||||
|
||||
[[deps.HttpCommon]]
|
||||
deps = ["Dates", "Nullables", "Test", "URIParser"]
|
||||
@@ -339,6 +338,12 @@ git-tree-sha1 = "5b62d93f2582b09e469b3099d839c2d2ebf5066d"
|
||||
uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
|
||||
version = "1.13.1"
|
||||
|
||||
[[deps.JWTs]]
|
||||
deps = ["Base64", "Downloads", "JSON", "MbedTLS", "Random"]
|
||||
git-tree-sha1 = "a1f3ded6307ef85cc18dec93d9b993814eb4c1a0"
|
||||
uuid = "d850fbd6-035d-5a70-a269-1ca2e636ac6c"
|
||||
version = "0.2.2"
|
||||
|
||||
[[deps.JuliaFormatter]]
|
||||
deps = ["CSTParser", "CommonMark", "DataStructures", "Glob", "Pkg", "PrecompileTools", "Tokenize"]
|
||||
git-tree-sha1 = "60567b51bd9e1e19ae2fd8a54dcd6bc5994727f0"
|
||||
@@ -386,10 +391,10 @@ deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
|
||||
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||
|
||||
[[deps.LibPQ]]
|
||||
deps = ["CEnum", "Dates", "Decimals", "DocStringExtensions", "FileWatching", "Infinity", "Intervals", "IterTools", "LayerDicts", "LibPQ_jll", "Libdl", "Memento", "OffsetArrays", "SQLStrings", "Tables", "TimeZones", "UTCDateTimes"]
|
||||
git-tree-sha1 = "114d9d239ab8e1251354ad6bb97ed38622133114"
|
||||
deps = ["CEnum", "DBInterface", "Dates", "Decimals", "DocStringExtensions", "FileWatching", "Infinity", "Intervals", "IterTools", "LayerDicts", "LibPQ_jll", "Libdl", "Memento", "OffsetArrays", "SQLStrings", "Tables", "TimeZones", "UTCDateTimes"]
|
||||
git-tree-sha1 = "d8967f68674aa9ad4b9b3df114e3842f269ac147"
|
||||
uuid = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1"
|
||||
version = "1.15.1"
|
||||
version = "1.16.0"
|
||||
|
||||
[[deps.LibPQ_jll]]
|
||||
deps = ["Artifacts", "JLLWrappers", "Kerberos_krb5_jll", "Libdl", "OpenSSL_jll", "Pkg"]
|
||||
@@ -519,9 +524,9 @@ version = "1.0.0"
|
||||
|
||||
[[deps.OffsetArrays]]
|
||||
deps = ["Adapt"]
|
||||
git-tree-sha1 = "82d7c9e310fe55aa54996e6f7f94674e2a38fcb4"
|
||||
git-tree-sha1 = "2ac17d29c523ce1cd38e27785a7d23024853a4bb"
|
||||
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
|
||||
version = "1.12.9"
|
||||
version = "1.12.10"
|
||||
|
||||
[[deps.OpenBLAS_jll]]
|
||||
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
|
||||
@@ -552,9 +557,9 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
|
||||
version = "0.5.5+0"
|
||||
|
||||
[[deps.OrderedCollections]]
|
||||
git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282"
|
||||
git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3"
|
||||
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||
version = "1.6.0"
|
||||
version = "1.6.2"
|
||||
|
||||
[[deps.Parameters]]
|
||||
deps = ["OrderedCollections", "UnPack"]
|
||||
@@ -564,9 +569,9 @@ version = "0.12.3"
|
||||
|
||||
[[deps.Parsers]]
|
||||
deps = ["Dates", "PrecompileTools", "UUIDs"]
|
||||
git-tree-sha1 = "4b2e829ee66d4218e0cef22c0a64ee37cf258c29"
|
||||
git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851"
|
||||
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
|
||||
version = "2.7.1"
|
||||
version = "2.7.2"
|
||||
|
||||
[[deps.Pkg]]
|
||||
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
|
||||
@@ -608,10 +613,10 @@ uuid = "21216c6a-2e73-6563-6e65-726566657250"
|
||||
version = "1.4.0"
|
||||
|
||||
[[deps.PrettyTables]]
|
||||
deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"]
|
||||
git-tree-sha1 = "213579618ec1f42dea7dd637a42785a608b1ea9c"
|
||||
deps = ["Crayons", "LaTeXStrings", "Markdown", "Printf", "Reexport", "StringManipulation", "Tables"]
|
||||
git-tree-sha1 = "ee094908d720185ddbdc58dbe0c1cbe35453ec7a"
|
||||
uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
|
||||
version = "2.2.4"
|
||||
version = "2.2.7"
|
||||
|
||||
[[deps.Printf]]
|
||||
deps = ["Unicode"]
|
||||
@@ -701,12 +706,6 @@ git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1"
|
||||
uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7"
|
||||
version = "1.1.0"
|
||||
|
||||
[[deps.SnoopPrecompile]]
|
||||
deps = ["Preferences"]
|
||||
git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c"
|
||||
uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c"
|
||||
version = "1.0.3"
|
||||
|
||||
[[deps.Sockets]]
|
||||
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
|
||||
|
||||
@@ -739,9 +738,9 @@ version = "1.9.0"
|
||||
|
||||
[[deps.StringEncodings]]
|
||||
deps = ["Libiconv_jll"]
|
||||
git-tree-sha1 = "33c0da881af3248dafefb939a21694b97cfece76"
|
||||
git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb"
|
||||
uuid = "69024149-9ee7-55f6-a4c4-859efe599b68"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
|
||||
[[deps.StringManipulation]]
|
||||
git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123"
|
||||
@@ -765,10 +764,10 @@ uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
|
||||
version = "1.0.3"
|
||||
|
||||
[[deps.TableShowUtils]]
|
||||
deps = ["DataValues", "Dates", "JSON", "Markdown", "Test"]
|
||||
git-tree-sha1 = "14c54e1e96431fb87f0d2f5983f090f1b9d06457"
|
||||
deps = ["DataValues", "Dates", "JSON", "Markdown", "Unicode"]
|
||||
git-tree-sha1 = "2a41a3dedda21ed1184a47caab56ed9304e9a038"
|
||||
uuid = "5e66a065-1f0a-5976-b372-e0b8c017ca10"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
|
||||
[[deps.TableTraits]]
|
||||
deps = ["IteratorInterfaceExtensions"]
|
||||
@@ -810,9 +809,9 @@ version = "1.0.1"
|
||||
|
||||
[[deps.TimeZones]]
|
||||
deps = ["Dates", "Downloads", "InlineStrings", "LazyArtifacts", "Mocking", "Printf", "RecipesBase", "Scratch", "Unicode"]
|
||||
git-tree-sha1 = "cdaa0c2a4449724aded839550eca7d7240bb6938"
|
||||
git-tree-sha1 = "5b347464bdac31eccfdbe1504d9484c31645cafc"
|
||||
uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.Tokenize]]
|
||||
git-tree-sha1 = "90538bf898832b6ebd900fa40f223e695970e3a5"
|
||||
@@ -880,7 +879,7 @@ version = "1.2.13+0"
|
||||
[[deps.libblastrampoline_jll]]
|
||||
deps = ["Artifacts", "Libdl"]
|
||||
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
|
||||
version = "5.7.0+0"
|
||||
version = "5.8.0+0"
|
||||
|
||||
[[deps.nghttp2_jll]]
|
||||
deps = ["Artifacts", "Libdl"]
|
||||
|
@@ -4,6 +4,7 @@ authors = ["a-ill <a_ill@outlook.com>"]
|
||||
version = "0.1.0"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
CSVFiles = "5d742f6a-9f54-50ce-8119-2520741973ca"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
@@ -15,6 +16,7 @@ GenieSession = "03cc5b98-4f21-4eb6-99f2-22eced81f962"
|
||||
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
Inflector = "6d011eab-0732-4556-8808-e463c76bf3b6"
|
||||
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
|
||||
JWTs = "d850fbd6-035d-5a70-a269-1ca2e636ac6c"
|
||||
LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1"
|
||||
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
|
||||
|
@@ -25,7 +25,11 @@
|
||||
<!--<loadscreen-component></loadscreen-component>-->
|
||||
|
||||
<div id="content">
|
||||
<navbar-component></navbar-component>
|
||||
<% if authenticated() %>
|
||||
<navbar-logged></navbar-logged>
|
||||
<% else %>
|
||||
<navbar-not-logged></navbar-not-logged>
|
||||
<% end %>
|
||||
|
||||
<%@yield%>
|
||||
|
||||
|
193
Server/app/resources/authentication/AuthenticationController.jl
Normal file
193
Server/app/resources/authentication/AuthenticationController.jl
Normal file
@@ -0,0 +1,193 @@
|
||||
module AuthenticationController
|
||||
|
||||
using Genie, Genie.Requests, Genie.Renderer, Genie.Renderer.Json, Genie.Renderer.Html, GenieSession, SearchLight, GenieAuthentication, GenieAuthorisation
|
||||
using Logging
|
||||
using JSON3, Random, Base64, HTTP, Dates
|
||||
using Server.Users, Server.EmailSupport, Server.TemplateEditor, Server.Cookies, Server.DatabaseSupport
|
||||
import Server.TemplateEditor.generate_layout_html
|
||||
import Server.DatabaseSupport.select_from_table
|
||||
using JWTs
|
||||
|
||||
#---Helpers----------------------------------------------------------
|
||||
|
||||
const keyset = JWKSet("https://www.googleapis.com/oauth2/v3/certs")
|
||||
refresh!(keyset)
|
||||
|
||||
current_user() = findone(Users.User, id = get_authentication())
|
||||
|
||||
function send_signup_confirmation_email(receiver,confirmation_code)
|
||||
subject,message = ["Sign-up confirmation","Hello!\r\nYour confirmation code is "*confirmation_code*"\r\n"]
|
||||
message = "Content-Type: text/html\r\n"*message
|
||||
return send_email(receiver,subject,message)
|
||||
end
|
||||
|
||||
function register_google()
|
||||
jws = rawpayload()
|
||||
jws_split = split(jws,".")
|
||||
payload_encoded = jws_split[2]
|
||||
rem = length(payload_encoded)%4
|
||||
if rem!= 0
|
||||
payload_encoded = payload_encoded* "="^(4-rem)
|
||||
end
|
||||
payload = String(base64decode(payload_encoded))
|
||||
json = JSON3.read(payload)
|
||||
sub = json[:sub]
|
||||
email = json[:email]
|
||||
user = findone(User, email = email)
|
||||
if isnothing(user)
|
||||
# ENABLE WHEN IN PRODUCTION
|
||||
user = User(email = email,google_id = sub) |> save!
|
||||
authenticate(user.id, GenieSession.session(params()))
|
||||
assign_role(user, findone(Role, name = "free"))
|
||||
save(user)
|
||||
return true
|
||||
return 0
|
||||
else
|
||||
jwt = JWT(payload="")
|
||||
jwt.header = jws_split[1]
|
||||
jwt.payload = jws_split[2]
|
||||
jwt.signature = jws_split[3]
|
||||
if validate!(jwt, keyset)
|
||||
if user.google_id==""
|
||||
user.google_id = sub
|
||||
save(user)
|
||||
authenticate(user.id, GenieSession.session(params()))
|
||||
return 3
|
||||
elseif user.google_id==sub
|
||||
authenticate(user.id, GenieSession.session(params()))
|
||||
return 3
|
||||
else
|
||||
return 0
|
||||
end
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function get_locale()
|
||||
data = payload()
|
||||
if :locale in keys(data)
|
||||
return data[:locale]
|
||||
else
|
||||
return "en"
|
||||
end
|
||||
end
|
||||
|
||||
const auth_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Login/Sign Up",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Логин/Регистрация",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
#---Routing functions---------------------------------------------------
|
||||
|
||||
controller = "authentication"
|
||||
const dict_layouts = Dict(
|
||||
:auth => generate_layout_html("main",controller,"auth",libraries=["GoogleAuth"]),
|
||||
:profile => generate_layout_html("main",controller,"profile",libraries=["Leaflet"]),
|
||||
:email_confirmation => generate_layout_html("main",controller,"email_confirmation"),
|
||||
)
|
||||
|
||||
function auth()
|
||||
locale = get_locale()
|
||||
set_cookies(params())
|
||||
html(:authentication,:auth, layout = dict_layouts[:auth], context = @__MODULE__,
|
||||
title = auth_info[locale][:title],
|
||||
description = auth_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function profile()
|
||||
set_cookies(params())
|
||||
html(:authentication,:profile, layout = dict_layouts[:profile], context = @__MODULE__,
|
||||
title = "Chiron | Profile",
|
||||
description = ""
|
||||
)
|
||||
end
|
||||
|
||||
function email_confirmation()
|
||||
set_cookies(params())
|
||||
html(:authentication,:email_confirmation, layout = dict_layouts[:email_confirmation], context = @__MODULE__,
|
||||
title = "Chiron | Email Confirmation",
|
||||
description = ""
|
||||
)
|
||||
end
|
||||
|
||||
function confirm_email()
|
||||
code = rawpayload()
|
||||
user = current_user()
|
||||
if code==user.confirmation_code
|
||||
GenieAuthorisation.Relationship!(user, findone(Role, name = "unconfirmed")) |> delete
|
||||
assign_role(user, findone(Role, name = "free"))
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function register()
|
||||
data = jsonpayload()
|
||||
user = findone(User, email = data["email"])
|
||||
if isnothing(user)
|
||||
user = User(email = data["email"],
|
||||
password = data["password"] |> Users.hash_password,
|
||||
) |> save!
|
||||
authenticate(user.id, GenieSession.session(params()))
|
||||
assign_role(user, findone(Role, name = "free"))
|
||||
confirmation_code = randstring('0':'9', 5)
|
||||
user.confirmation_code = confirmation_code
|
||||
save(user)
|
||||
#send_signup_confirmation_email(data["email"],confirmation_code)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function login()
|
||||
data = jsonpayload()
|
||||
user = findone(User, email = data["email"])
|
||||
if isnothing(user)
|
||||
return 0
|
||||
else
|
||||
if (user.password==Users.hash_password(data["password"]))
|
||||
authenticate(user.id, GenieSession.session(params()))
|
||||
return 2
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function logout()
|
||||
deauthenticate(GenieSession.session(params()))
|
||||
return
|
||||
end
|
||||
|
||||
function change_user()
|
||||
data = jsonpayload()
|
||||
user = findone(Users.User, id = get_authentication())
|
||||
for (field,value) in data
|
||||
setfield!(user, Symbol(field), value)
|
||||
end
|
||||
save(user)
|
||||
return JSON3.write(true)
|
||||
end
|
||||
|
||||
function get_user()
|
||||
try
|
||||
user = findone(Users.User, id = get_authentication())
|
||||
return JSON3.write(user)
|
||||
catch ex
|
||||
return JSON3.write(false)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
2
Server/app/resources/authentication/views/auth.jl.html
Normal file
2
Server/app/resources/authentication/views/auth.jl.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<auth-component></auth-component>
|
||||
|
@@ -0,0 +1 @@
|
||||
<confirmation-component></confirmation-component>
|
@@ -0,0 +1,2 @@
|
||||
|
||||
<profile-component></profile-component>
|
@@ -1,6 +1,6 @@
|
||||
module BasicController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
|
||||
using JSON3
|
||||
using SearchLight
|
||||
using Server.DatabaseSupport, Server.TemplateEditor
|
||||
@@ -10,11 +10,6 @@ dict_layouts = Dict(
|
||||
:landing => generate_layout_html("main",controller,"landing",css=["landing"],libraries=["Leaflet"]),
|
||||
:manifesto => generate_layout_html("main",controller,"manifesto"),
|
||||
:join_us => generate_layout_html("main",controller,"join_us",libraries=["Leaflet"]),
|
||||
:groups => generate_layout_html("main",controller,"groups",libraries=["Leaflet"]),
|
||||
:cooperatives => generate_layout_html("main",controller,"cooperatives",libraries=["Leaflet"]),
|
||||
:communes => generate_layout_html("main",controller,"communes",libraries=["Leaflet"]),
|
||||
:parties => generate_layout_html("main",controller,"parties",libraries=["Leaflet"]),
|
||||
:partners => generate_layout_html("main",controller,"partners",libraries=["Leaflet"]),
|
||||
:compass => generate_layout_html("main",controller,"compass"),
|
||||
)
|
||||
|
||||
@@ -53,61 +48,6 @@ const join_us_info = Dict(
|
||||
),
|
||||
)
|
||||
|
||||
const groups_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Groups",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Группы",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
const cooperatives_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Cooperatives",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Кооперативы",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
const communes_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Communes",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Коммуны",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
const partners_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Partners",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Партнеры",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
const parties_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Parties",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Партии",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
const compass_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Political Compass",
|
||||
@@ -146,54 +86,6 @@ function manifesto()
|
||||
)
|
||||
end
|
||||
|
||||
function join_us()
|
||||
locale = get_locale()
|
||||
html(:basic,:join_us, layout = dict_layouts[:join_us], context = @__MODULE__,
|
||||
title = join_us_info[locale][:title],
|
||||
description = join_us_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function groups()
|
||||
locale = get_locale()
|
||||
html(:basic,:groups, layout = dict_layouts[:groups], context = @__MODULE__,
|
||||
title = groups_info[locale][:title],
|
||||
description = groups_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function cooperatives()
|
||||
locale = get_locale()
|
||||
html(:basic,:cooperatives, layout = dict_layouts[:cooperatives], context = @__MODULE__,
|
||||
title = cooperatives_info[locale][:title],
|
||||
description = cooperatives_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function communes()
|
||||
locale = get_locale()
|
||||
html(:basic,:communes, layout = dict_layouts[:communes], context = @__MODULE__,
|
||||
title = communes_info[locale][:title],
|
||||
description = communes_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function partners()
|
||||
locale = get_locale()
|
||||
html(:basic,:partners, layout = dict_layouts[:partners], context = @__MODULE__,
|
||||
title = partners_info[locale][:title],
|
||||
description = partners_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function parties()
|
||||
locale = get_locale()
|
||||
html(:basic,:parties, layout = dict_layouts[:parties], context = @__MODULE__,
|
||||
title = parties_info[locale][:title],
|
||||
description = parties_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function political_compass()
|
||||
locale = get_locale()
|
||||
html(:basic,:compass, layout = dict_layouts[:compass], context = @__MODULE__,
|
||||
@@ -202,4 +94,12 @@ function political_compass()
|
||||
)
|
||||
end
|
||||
|
||||
function join_us()
|
||||
locale = get_locale()
|
||||
html(:basic,:join_us, layout = dict_layouts[:join_us], context = @__MODULE__,
|
||||
title = join_us_info[locale][:title],
|
||||
description = join_us_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
end
|
59
Server/app/resources/communes/CommunesController.jl
Normal file
59
Server/app/resources/communes/CommunesController.jl
Normal file
@@ -0,0 +1,59 @@
|
||||
module CommunesController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
|
||||
using JSON3
|
||||
using SearchLight
|
||||
using Server.DatabaseSupport, Server.TemplateEditor
|
||||
|
||||
controller = "communes"
|
||||
dict_layouts = Dict(
|
||||
:communes => generate_layout_html("main",controller,"communes",libraries=["Leaflet"]),
|
||||
:communes_add => generate_layout_html("main",controller,"communes_add",libraries=["Leaflet"]),
|
||||
)
|
||||
|
||||
#---Page info-----------------------------------------------------
|
||||
|
||||
const communes_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Communes",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Коммуны",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
function get_locale()
|
||||
data = payload()
|
||||
if :locale in keys(data)
|
||||
return data[:locale]
|
||||
else
|
||||
return "en"
|
||||
end
|
||||
end
|
||||
|
||||
#---Functions---------------------------------------------------------
|
||||
|
||||
function communes()
|
||||
locale = get_locale()
|
||||
html(:communes,:communes, layout = dict_layouts[:communes], context = @__MODULE__,
|
||||
title = communes_info[locale][:title],
|
||||
description = communes_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function communes_add()
|
||||
locale = get_locale()
|
||||
html(:communes,:communes_add, layout = dict_layouts[:communes_add], context = @__MODULE__,
|
||||
title = communes_info[locale][:title],
|
||||
description = communes_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function communes_add_post()
|
||||
data = jsonpayload()
|
||||
insert_into_table("communes_requests",data)
|
||||
end
|
||||
|
||||
end
|
1
Server/app/resources/communes/views/communes_add.jl.html
Normal file
1
Server/app/resources/communes/views/communes_add.jl.html
Normal file
@@ -0,0 +1 @@
|
||||
<communes-add-component></communes-add-component>
|
60
Server/app/resources/cooperatives/CooperativesController.jl
Normal file
60
Server/app/resources/cooperatives/CooperativesController.jl
Normal file
@@ -0,0 +1,60 @@
|
||||
module CooperativesController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
|
||||
using JSON3
|
||||
using SearchLight
|
||||
using Server.DatabaseSupport, Server.TemplateEditor
|
||||
|
||||
controller = "cooperatives"
|
||||
dict_layouts = Dict(
|
||||
:cooperatives => generate_layout_html("main",controller,"cooperatives",libraries=["Leaflet"]),
|
||||
:cooperatives_add => generate_layout_html("main",controller,"cooperatives_add",libraries=["Leaflet"]),
|
||||
)
|
||||
|
||||
#---Page info-----------------------------------------------------
|
||||
|
||||
const cooperatives_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Cooperatives",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Кооперативы",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
function get_locale()
|
||||
data = payload()
|
||||
if :locale in keys(data)
|
||||
return data[:locale]
|
||||
else
|
||||
return "en"
|
||||
end
|
||||
end
|
||||
|
||||
#---Functions---------------------------------------------------------
|
||||
|
||||
|
||||
function cooperatives_add()
|
||||
locale = get_locale()
|
||||
html(:cooperatives,:cooperatives_add, layout = dict_layouts[:cooperatives_add], context = @__MODULE__,
|
||||
title = cooperatives_info[locale][:title],
|
||||
description = cooperatives_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function cooperatives()
|
||||
locale = get_locale()
|
||||
html(:cooperatives,:cooperatives, layout = dict_layouts[:cooperatives], context = @__MODULE__,
|
||||
title = cooperatives_info[locale][:title],
|
||||
description = cooperatives_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function cooperatives_add_post()
|
||||
data = jsonpayload()
|
||||
insert_into_table("cooperatives_requests",data)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1 @@
|
||||
<cooperatives-add-component></cooperatives-add-component>
|
261
Server/app/resources/groups/GroupsController.jl
Normal file
261
Server/app/resources/groups/GroupsController.jl
Normal file
@@ -0,0 +1,261 @@
|
||||
module GroupsController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication, DataFrames
|
||||
using JSON3
|
||||
using SearchLight,SearchLightPostgreSQL, LibPQ, JSON3
|
||||
using Server.DatabaseSupport, Server.TemplateEditor, Server.Users
|
||||
import Server.DatabaseSupport: select_from_table, insert_into_table, delete_from_table, exist_in_table
|
||||
|
||||
controller = "groups"
|
||||
dict_layouts = Dict(
|
||||
:groups => generate_layout_html("main",controller,"groups",libraries=["Leaflet"]),
|
||||
:groups_add => generate_layout_html("main",controller,"groups_add",libraries=["Leaflet"]),
|
||||
)
|
||||
|
||||
#---Page info-----------------------------------------------------
|
||||
|
||||
const groups_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Groups",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Группы",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
function get_locale()
|
||||
data = payload()
|
||||
if :locale in keys(data)
|
||||
return data[:locale]
|
||||
else
|
||||
return "en"
|
||||
end
|
||||
end
|
||||
|
||||
#---Helpers-----------------------------------------------------------
|
||||
|
||||
|
||||
function table_to_json(name,df)
|
||||
ar = []
|
||||
for df_row in eachrow(df)
|
||||
dict = Dict()
|
||||
for id in names(df_row)
|
||||
dict[id] = df_row[id]
|
||||
end
|
||||
push!(ar,dict)
|
||||
end
|
||||
open("public/assets/"*name*".json", "w") do io
|
||||
JSON3.write(io, ar)
|
||||
end
|
||||
end
|
||||
|
||||
function compile(name)
|
||||
df = select_from_table([name => ["*"]])
|
||||
table_to_json(name,df)
|
||||
end
|
||||
|
||||
function move_requests(name)
|
||||
df_requests = select_from_table(["$(name)_requests" => ["*"]], where_data=["verified" => true, "added" => false])
|
||||
df = select_from_table([name => ["*"]])
|
||||
latitudes = df.latitude
|
||||
longitudes = df.longitude
|
||||
for df_row in eachrow(df_requests)
|
||||
ind_id_given = ismissing(df_row.id_given) ? nothing : findfirst(df_row.id_given.==df.id)
|
||||
if (!isnothing(ind_id_given))
|
||||
id = df[ind_id_given,:id]
|
||||
row_found = df[ind_id_given,Not(:id)]
|
||||
dict = Dict(zip(names(row_found),values(row_found)))
|
||||
dict["members"] += 1
|
||||
update_table(name,dict, where_data=["id" => id])
|
||||
else
|
||||
id = df_row.id
|
||||
dict_update = Dict("added" => true)
|
||||
update_table("$(name)_requests",dict_update, where_data=["id" => id])
|
||||
|
||||
df_row_to_add = df_row[Not(:id_given)]
|
||||
df_row_to_add = df_row_to_add[Not(:verified)]
|
||||
df_row_to_add = df_row_to_add[Not(:added)]
|
||||
df_row_to_add = df_row_to_add[Not(:id)]
|
||||
dict = Dict(zip(names(df_row_to_add),values(df_row_to_add)))
|
||||
dict["members"] = 1
|
||||
insert_into_table(name,dict)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#---Functions---------------------------------------------------------
|
||||
|
||||
current_user() = findone(Users.User, id = get_authentication())
|
||||
|
||||
function groups()
|
||||
locale = get_locale()
|
||||
html(:groups,:groups, layout = dict_layouts[:groups], context = @__MODULE__,
|
||||
title = groups_info[locale][:title],
|
||||
description = groups_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function groups_add()
|
||||
locale = get_locale()
|
||||
html(:groups,:groups_add, layout = dict_layouts[:groups_add], context = @__MODULE__,
|
||||
title = groups_info[locale][:title],
|
||||
description = groups_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function groups_add_post()
|
||||
data = copy(jsonpayload())
|
||||
mode = data["mode"]
|
||||
delete!(data,"mode")
|
||||
user = current_user()
|
||||
user_id = user.id
|
||||
if mode==0 # Create
|
||||
if user.verified
|
||||
existing_user_group_data = select_from_table(["users_groups" => ["*"]], where_data=["user_id" => user_id])
|
||||
has_group = !isempty(existing_user_group_data)
|
||||
delete!(data,"group_id")
|
||||
group_id = insert_into_table("groups",data, "RETURNING id")[1,1]
|
||||
if has_group
|
||||
user_groups_id = existing_user_group_data[1,"id"]
|
||||
prev_group_id = existing_user_group_data[1,"group_id"]
|
||||
update_table("users_groups",Dict("group_id" => group_id), where_data=["id" => user_groups_id])
|
||||
members = select_from_table(["groups" => ["members"]], where_data=["id" => prev_group_id])[1,1]
|
||||
if (members==1)
|
||||
delete_from_table("groups",["id" => prev_group_id])
|
||||
else
|
||||
update_table("groups",Dict("members" => members - 1), where_data=["id" => id])
|
||||
end
|
||||
else
|
||||
dict_users_groups = Dict("user_id" => user.id, "group_id" => group_id)
|
||||
insert_into_table("users_groups",dict_users_groups)
|
||||
end
|
||||
compile("groups")
|
||||
else
|
||||
data["status"] = 0
|
||||
data["user_id"] = user_id
|
||||
insert_into_table("groups_requests",data)
|
||||
end
|
||||
elseif mode==1 # Join
|
||||
data["user_id"] = user_id
|
||||
if exist_in_table("users_groups",["group_id" => data["group_id"]])
|
||||
if exist_in_table("groups_requests",["user_id" => user_id])
|
||||
delete_from_table("groups_requests",["user_id" => user_id])
|
||||
end
|
||||
data["status"] = 0
|
||||
insert_into_table("groups_requests",data)
|
||||
else
|
||||
group_id = data["group_id"]
|
||||
members = select_from_table("groups" => ["members"], where_data = ["id" => group_id])[1,1]
|
||||
dict = Dict("members" => members + 1)
|
||||
update_table("groups",dict, where_data=["id" => group_id])
|
||||
dict_users_groups = Dict("user_id" => user_id, "group_id" => group_id)
|
||||
insert_into_table("users_groups",dict_users_groups)
|
||||
end
|
||||
|
||||
elseif mode==2 # Move
|
||||
existing_user_group_data = select_from_table(["users_groups" => ["*"]], where_data=["user_id" => user_id])
|
||||
group_id = existing_user_group_data[1,"group_id"]
|
||||
delete!(data,"group_id")
|
||||
delete!(data,"members")
|
||||
delete!(data,"contact")
|
||||
update_table("groups",data, where_data=["id" => group_id])
|
||||
compile("groups")
|
||||
elseif mode==3 # Leave
|
||||
existing_user_group_data = select_from_table(["users_groups" => ["*"]], where_data=["user_id" => user_id])
|
||||
if size(existing_user_group_data,1)==0
|
||||
if exist_in_table("groups_requests",["user_id" => user_id])
|
||||
delete_from_table("groups_requests",["user_id" => user_id])
|
||||
end
|
||||
else
|
||||
delete_from_table("users_groups",["user_id" => user_id])
|
||||
end
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
function get_user_groups()
|
||||
local data_dicts
|
||||
user_id = get_authentication()
|
||||
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
|
||||
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
|
||||
data_dicts = []
|
||||
if isnothing(group_id)
|
||||
local data
|
||||
data = select_from_table("groups_requests" => ["*"], where_data = ["user_id" => user_id,"status" => 0])
|
||||
if size(data,1)==0
|
||||
data = select_from_table("groups_requests" => ["*"], where_data = ["user_id" => user_id,"status" => 2])
|
||||
if size(data,1)!=0
|
||||
data = data[[end],:]
|
||||
end
|
||||
end
|
||||
for row in eachrow(data)
|
||||
dict = Dict(zip(names(row),values(row)))
|
||||
if (!ismissing(row["group_id"]))
|
||||
extra_data = select_from_table("groups" => ["*"], where_data = ["id" => row["group_id"]])
|
||||
merge!(dict, Dict(zip(names(extra_data[1,:]),values(extra_data[1,:]))))
|
||||
end
|
||||
push!(data_dicts, dict)
|
||||
end
|
||||
else
|
||||
group_data = select_from_table("groups" => ["*"], where_data = ["id" => group_id])
|
||||
ns = names(group_data)
|
||||
data_dicts = map(x -> Dict(zip(ns,values(x))),eachrow(group_data))
|
||||
end
|
||||
return JSON3.write(data_dicts)
|
||||
end
|
||||
|
||||
function get_group_requests()
|
||||
user_id = get_authentication()
|
||||
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
|
||||
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
|
||||
data_dicts = []
|
||||
if !isnothing(group_id)
|
||||
user_ids = select_from_table("groups_requests" => ["user_id"], where_data = ["group_id" => group_id, "status" => 0])[:,1]
|
||||
for user2_id in user_ids
|
||||
email = select_from_table("users" => ["email"], where_data = ["id" => user2_id])[1,1]
|
||||
push!(data_dicts,Dict("email" => email, "user_id" => user2_id))
|
||||
end
|
||||
end
|
||||
return JSON3.write(data_dicts)
|
||||
end
|
||||
|
||||
function approve_request()
|
||||
data = copy(jsonpayload())
|
||||
user_id = get_authentication()
|
||||
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
|
||||
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
|
||||
members = select_from_table("groups" => ["members"], where_data = ["id" => group_id])[1,1]
|
||||
dict = Dict("members" => members + 1)
|
||||
update_table("groups",dict, where_data=["id" => group_id])
|
||||
update_table("groups_requests",Dict("status" => 1), where_data=["group_id" => group_id, "user_id" => data["user_id"]])
|
||||
dict_users_groups = Dict("user_id" => data["user_id"], "group_id" => group_id)
|
||||
insert_into_table("users_groups",dict_users_groups)
|
||||
return nothing
|
||||
end
|
||||
|
||||
function reject_request()
|
||||
data = copy(jsonpayload())
|
||||
user_id = get_authentication()
|
||||
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
|
||||
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
|
||||
update_table("groups_requests",Dict("status" => 2), where_data=["group_id" => group_id, "user_id" => data["user_id"]])
|
||||
return nothing
|
||||
end
|
||||
|
||||
function add_verified_groups()
|
||||
groups_create_requests_verified = select_from_table("groups_requests" => ["*"], where_data = ["group_id" => nothing, "status" => 1])
|
||||
data = Dict(zip(names(groups_create_requests_verified),groups_create_requests_verified[end,:]))
|
||||
user_id = data["user_id"]
|
||||
delete!(data,"group_id")
|
||||
delete!(data,"user_id")
|
||||
delete!(data,"id")
|
||||
delete!(data,"status")
|
||||
group_id = insert_into_table("groups",data, "RETURNING id")[1,1]
|
||||
dict_users_groups = Dict("user_id" => user_id, "group_id" => group_id)
|
||||
insert_into_table("users_groups",dict_users_groups)
|
||||
delete_from_table("groups_requests",["user_id" => user_id])
|
||||
end
|
||||
|
||||
end
|
1
Server/app/resources/groups/views/groups_add.jl.html
Normal file
1
Server/app/resources/groups/views/groups_add.jl.html
Normal file
@@ -0,0 +1 @@
|
||||
<groups-add-component></groups-add-component>
|
60
Server/app/resources/parties/PartiesController.jl
Normal file
60
Server/app/resources/parties/PartiesController.jl
Normal file
@@ -0,0 +1,60 @@
|
||||
module PartiesController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
|
||||
using JSON3
|
||||
using SearchLight
|
||||
using Server.DatabaseSupport, Server.TemplateEditor
|
||||
|
||||
controller = "parties"
|
||||
dict_layouts = Dict(
|
||||
:parties_add => generate_layout_html("main",controller,"parties_add",libraries=["Leaflet"]),
|
||||
:parties => generate_layout_html("main",controller,"parties",libraries=["Leaflet"]),
|
||||
)
|
||||
|
||||
#---Page info-----------------------------------------------------
|
||||
|
||||
const parties_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Parties",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Партии",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
function get_locale()
|
||||
data = payload()
|
||||
if :locale in keys(data)
|
||||
return data[:locale]
|
||||
else
|
||||
return "en"
|
||||
end
|
||||
end
|
||||
|
||||
#---Functions---------------------------------------------------------
|
||||
|
||||
|
||||
function parties()
|
||||
locale = get_locale()
|
||||
html(:parties,:parties, layout = dict_layouts[:parties], context = @__MODULE__,
|
||||
title = parties_info[locale][:title],
|
||||
description = parties_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function parties_add()
|
||||
locale = get_locale()
|
||||
html(:parties,:parties_add, layout = dict_layouts[:parties_add], context = @__MODULE__,
|
||||
title = parties_info[locale][:title],
|
||||
description = parties_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function parties_add_post()
|
||||
data = jsonpayload()
|
||||
insert_into_table("parties_requests",data)
|
||||
end
|
||||
|
||||
end
|
1
Server/app/resources/parties/views/parties_add.jl.html
Normal file
1
Server/app/resources/parties/views/parties_add.jl.html
Normal file
@@ -0,0 +1 @@
|
||||
<parties-add-component></parties-add-component>
|
59
Server/app/resources/partners/PartnersController.jl
Normal file
59
Server/app/resources/partners/PartnersController.jl
Normal file
@@ -0,0 +1,59 @@
|
||||
module PartnersController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
|
||||
using JSON3
|
||||
using SearchLight
|
||||
using Server.DatabaseSupport, Server.TemplateEditor
|
||||
|
||||
controller = "partners"
|
||||
dict_layouts = Dict(
|
||||
:partners => generate_layout_html("main",controller,"partners",libraries=["Leaflet"]),
|
||||
:partners_add => generate_layout_html("main",controller,"partners_add",libraries=["Leaflet"]),
|
||||
)
|
||||
|
||||
#---Page info-----------------------------------------------------
|
||||
|
||||
const partners_info = Dict(
|
||||
"en" => Dict(
|
||||
:title => "LibSoc - Partners",
|
||||
:description => ""
|
||||
),
|
||||
"ru" => Dict(
|
||||
:title => "LibSoc - Партнеры",
|
||||
:description => ""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
function get_locale()
|
||||
data = payload()
|
||||
if :locale in keys(data)
|
||||
return data[:locale]
|
||||
else
|
||||
return "en"
|
||||
end
|
||||
end
|
||||
|
||||
#---Functions---------------------------------------------------------
|
||||
|
||||
function partners()
|
||||
locale = get_locale()
|
||||
html(:partners,:partners, layout = dict_layouts[:partners], context = @__MODULE__,
|
||||
title = partners_info[locale][:title],
|
||||
description = partners_info[locale][:description]
|
||||
)
|
||||
end
|
||||
function partners_add()
|
||||
locale = get_locale()
|
||||
html(:partners,:partners_add, layout = dict_layouts[:partners_add], context = @__MODULE__,
|
||||
title = partners_info[locale][:title],
|
||||
description = partners_info[locale][:description]
|
||||
)
|
||||
end
|
||||
|
||||
function partners_add_post()
|
||||
data = jsonpayload()
|
||||
insert_into_table("partners_requests",data)
|
||||
end
|
||||
|
||||
end
|
1
Server/app/resources/partners/views/partners_add.jl.html
Normal file
1
Server/app/resources/partners/views/partners_add.jl.html
Normal file
@@ -0,0 +1 @@
|
||||
<partners-add-component></partners-add-component>
|
29
Server/app/resources/users/Users.jl
Normal file
29
Server/app/resources/users/Users.jl
Normal file
@@ -0,0 +1,29 @@
|
||||
module Users
|
||||
|
||||
using SearchLight, SearchLight.Validation, Server.UsersValidator
|
||||
using SHA
|
||||
using Random
|
||||
|
||||
export User
|
||||
|
||||
Base.@kwdef mutable struct User <: AbstractModel
|
||||
### FIELDS
|
||||
id::DbId = DbId()
|
||||
email::String = ""
|
||||
password::String = ""
|
||||
google_id::String = ""
|
||||
confirmation_code::String = ""
|
||||
verified::Bool = false
|
||||
end
|
||||
|
||||
Validation.validator(u::Type{User}) = ModelValidator([
|
||||
ValidationRule(:email, UsersValidator.not_empty),
|
||||
ValidationRule(:email, UsersValidator.unique),
|
||||
ValidationRule(:password, UsersValidator.not_empty)
|
||||
])
|
||||
|
||||
function hash_password(password::String)
|
||||
sha256(password) |> bytes2hex
|
||||
end
|
||||
|
||||
end
|
21
Server/app/resources/users/UsersValidator.jl
Normal file
21
Server/app/resources/users/UsersValidator.jl
Normal file
@@ -0,0 +1,21 @@
|
||||
module UsersValidator
|
||||
|
||||
using SearchLight, SearchLight.Validation, SearchLight.QueryBuilder
|
||||
|
||||
function not_empty(field::Symbol, m::T, args::Vararg{Any})::ValidationResult where {T<:AbstractModel}
|
||||
isempty(getfield(m, field)) && return ValidationResult(invalid, :not_empty, "should not be empty")
|
||||
|
||||
ValidationResult(valid)
|
||||
end
|
||||
|
||||
function unique(field::Symbol, m::T, args::Vararg{Any})::ValidationResult where {T<:AbstractModel}
|
||||
ispersisted(m) && return ValidationResult(valid) # don't validate updates
|
||||
|
||||
if SearchLight.count(typeof(m), where("$field = ?", getfield(m, field))) > 0
|
||||
return ValidationResult(invalid, :unique, "is already used")
|
||||
end
|
||||
|
||||
ValidationResult(valid)
|
||||
end
|
||||
|
||||
end
|
180
Server/app/svelte/public/css/auth.css
Normal file
180
Server/app/svelte/public/css/auth.css
Normal file
@@ -0,0 +1,180 @@
|
||||
|
||||
label {
|
||||
font-size: 1.3rem;
|
||||
font-family: var(--sans-serif);
|
||||
}
|
||||
|
||||
.auth-pane {
|
||||
position: relative;
|
||||
padding: 3.4rem;
|
||||
padding-top: 3.4rem;
|
||||
padding-bottom: 3.4rem;
|
||||
width: 30rem;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
position: relative;
|
||||
top: 0.2rem;
|
||||
margin-bottom: 2.7rem;
|
||||
}
|
||||
|
||||
.auth-label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.authEmailInput, .authPasswordInput {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border-radius: 0.34rem;
|
||||
color: #353535;
|
||||
height: 2.73rem;
|
||||
padding-left: 0.34rem;
|
||||
}
|
||||
|
||||
.authEmailInput {
|
||||
margin-bottom: 0.682rem;
|
||||
}
|
||||
|
||||
.auth-button {
|
||||
margin-top: 1.365rem;
|
||||
height: 3.412rem;
|
||||
width: 100%;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.3rem;
|
||||
color: white;
|
||||
background-color: var(--red);
|
||||
border-color: var(--red);
|
||||
border-radius: 0.512rem;
|
||||
filter: drop-shadow(0.068rem 0.136rem 0.068rem rgb(0 0 0 / 0.4));
|
||||
}
|
||||
|
||||
.auth-button:active {
|
||||
background-color: var(--darker-pink);
|
||||
}
|
||||
|
||||
#email-msg,#password-msg {
|
||||
display: inline;
|
||||
color:red;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
}
|
||||
|
||||
.auth-line {
|
||||
margin-top: 1.5rem;
|
||||
width: 100%;
|
||||
height: 0.07rem;
|
||||
border: 0;
|
||||
border-radius: 0.1rem;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.auth-methods-group {
|
||||
display: grid;
|
||||
grid-template-columns: auto ; /*auto auto*/
|
||||
justify-content: center;
|
||||
gap: 2.7rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.auth-methods-group img {
|
||||
height: auto;
|
||||
width: 3.4rem;
|
||||
}
|
||||
|
||||
.auth-methods-group> div {
|
||||
position: relative;
|
||||
border-radius: 6.8rem;
|
||||
width: 3.4rem;
|
||||
height: 3.4rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#google-btn {
|
||||
position: absolute;
|
||||
top: -0.8rem;
|
||||
left: -0.8rem;
|
||||
}
|
||||
|
||||
#google-logo {
|
||||
position: relative;
|
||||
background: white;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#google-btn-wrapper {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#google-btn div {
|
||||
position: absolute;
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
#google-btn iframe {
|
||||
position: absolute;
|
||||
height: 10rem;
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
.password-field {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.eye-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
opacity: 0.25;
|
||||
top: 2.6rem;
|
||||
right: 0.8rem;
|
||||
width: 1.7rem;
|
||||
}
|
||||
|
||||
.eye-icon * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#forgot-password {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin-top: 0.5rem;
|
||||
height: 2rem;
|
||||
color:#5f5f5f;
|
||||
margin-left: auto;
|
||||
width: max-content;
|
||||
font-family: var(--sans-serif);
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
|
||||
#remember-me {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#remember-me-checkbox {
|
||||
min-height: 1.5rem;
|
||||
min-width: 1.5rem;
|
||||
flex: 0;
|
||||
accent-color: var(--gray);
|
||||
}
|
||||
|
||||
#remember-me label {
|
||||
position: relative;
|
||||
margin-top: -0.2rem;
|
||||
color: #5f5f5f;
|
||||
}
|
||||
|
||||
#content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
@@ -1,21 +1,7 @@
|
||||
|
||||
:root {
|
||||
--light-blue:hsl(195, 67%, 95%);
|
||||
--darker-pink:hsl(344, 60%, 47%);
|
||||
--pink:hsl(344, 73%, 57%);
|
||||
--dark-green:hsl(176, 63%, 25%);
|
||||
--green:hsl(147, 33%, 60%);
|
||||
--orange:hsl(30, 97%, 72%);
|
||||
--light-orange: hsl(19, 76%, 72%);
|
||||
--dark-brown:hsl(23, 47%, 20%);
|
||||
--brown:hsl(23, 47%, 30%);
|
||||
--light-brown: hsl(23, 47%, 50%);
|
||||
--dark-pink:hsl(343, 39%, 16%);
|
||||
--red:hsl(359, 72%, 61%);
|
||||
--dark-blue:hsl(217, 25%, 16%);
|
||||
--grey-blue:hsl(223, 13%, 22%);
|
||||
--cream:hsl(34, 43%, 90%);
|
||||
--dark-cream:hsl(33, 26%, 84%);
|
||||
--red:#c52a28;
|
||||
--gray: #5B6970;
|
||||
--sans-serif: "OpenSans";
|
||||
--serif: "Lora";
|
||||
}
|
||||
@@ -48,12 +34,10 @@ body {
|
||||
|
||||
#content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-template-rows: max-content auto max-content;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/*---Fonts---------------------------------------------------------*/
|
||||
@@ -310,7 +294,7 @@ input[type="text"],input[type="email"],input[type="password"],input[type="number
|
||||
border-color: black;
|
||||
border: black solid 0.063rem;
|
||||
padding-left: 0.5rem;
|
||||
font: 1.3rem var(--serif, serif);
|
||||
font: 1.15rem var(--serif, serif);
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
@@ -475,9 +459,8 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
|
||||
.pane {
|
||||
background: white;
|
||||
border: 0;
|
||||
border: 0.1rem solid rgb(187, 187, 187);
|
||||
border-radius: 0.635rem;
|
||||
box-shadow: 0 0 0.314rem rgb(187, 187, 187);
|
||||
}
|
||||
|
||||
.pane-container {
|
||||
|
268
Server/app/svelte/public/css/navbar.css
Normal file
268
Server/app/svelte/public/css/navbar.css
Normal file
@@ -0,0 +1,268 @@
|
||||
/* Header */
|
||||
#navbar{
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: min(100%,116rem);
|
||||
z-index: 1000000000;
|
||||
height: 5.26rem;
|
||||
padding-left: 0rem;
|
||||
padding-right: 0rem;
|
||||
}
|
||||
|
||||
#navbar * {
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
#logo-container {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
margin-left: 1rem;
|
||||
height: 100%;
|
||||
max-height: 5.26rem;
|
||||
color: black;
|
||||
z-index: 1;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#navbar-logo {
|
||||
height: 3.5rem;
|
||||
width: 3.5rem;
|
||||
object-fit: contain;
|
||||
border-radius: 10rem;
|
||||
}
|
||||
|
||||
#navbar-logo-text {
|
||||
position: relative;
|
||||
word-wrap: normal;
|
||||
height: 100%;
|
||||
line-height: 400%;
|
||||
font-size: 1.4rem;
|
||||
color: #292222;
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-weight: 400;
|
||||
padding-left: 1.2rem;
|
||||
}
|
||||
|
||||
/* Nav menu */
|
||||
#nav {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
}
|
||||
#menu > li > a, .options-button {
|
||||
display: block;
|
||||
padding: 1.2rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
color: black;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
#menu > li > a:active{
|
||||
|
||||
}
|
||||
|
||||
#menu li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#nav{
|
||||
max-height: 0;
|
||||
/*transition: max-height .5s ease-out;*/
|
||||
}
|
||||
|
||||
/* Menu Icon */
|
||||
#hamb{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
right: 0rem;
|
||||
padding: 2.8rem 2rem;
|
||||
z-index: 9999;
|
||||
}/* Style label tag */
|
||||
|
||||
#hamb-line {
|
||||
background: black;
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: relative;
|
||||
width: 24px;
|
||||
|
||||
} /* Style span tag */
|
||||
|
||||
#hamb-line::before,
|
||||
#hamb-line::after{
|
||||
background: black;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: all .2s ease-out;
|
||||
width: 100%;
|
||||
}
|
||||
#hamb-line::before{
|
||||
top: 5px;
|
||||
}
|
||||
#hamb-line::after{
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
#side-menu {
|
||||
display: none;
|
||||
} /* Hide checkbox */
|
||||
|
||||
/* Toggle menu icon */
|
||||
#side-menu:checked ~ nav {
|
||||
display: block;
|
||||
max-height: 100%;
|
||||
padding-top: 5.625rem;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #logo-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #logo-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line::before {
|
||||
transform: rotate(-45deg);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line::after {
|
||||
transform: rotate(45deg);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Options */
|
||||
|
||||
.options-dropdown {
|
||||
position: absolute;
|
||||
display: none;
|
||||
top: 5.6rem;
|
||||
right: 1.8rem;
|
||||
border: #404040 solid 0.1rem;
|
||||
background-color: white;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.options-dropdown button, .options-dropdown a {
|
||||
display: block;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.2rem;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.options-dropdown button:hover, .options-dropdown a:hover {
|
||||
background-color: var(--red);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.options-button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Localization */
|
||||
|
||||
#locales {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#locales button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
#locales button:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#locales-img {
|
||||
position: relative;
|
||||
top: 0rem;
|
||||
height: 2rem;
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
|
||||
/*
|
||||
#options-dropdown>:first-child {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#options-dropdown>:nth-child(2) {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Responsiveness */
|
||||
@media only screen and (min-width: 1200px) {
|
||||
|
||||
#navbar {
|
||||
position: relative;
|
||||
width: min(100%,116rem);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding-right: 4rem;
|
||||
padding-left: 4rem;
|
||||
}
|
||||
|
||||
#nav {
|
||||
max-height: none;
|
||||
top: 0;
|
||||
position: relative;
|
||||
float: right;
|
||||
width: fit-content;
|
||||
background-color: transparent;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ nav {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#menu li {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#menu > li > a:hover, .options-button:hover, #navbar-logo-text:hover {
|
||||
color: rgb(127, 127, 127);
|
||||
}
|
||||
|
||||
#menu > li > a, .options-button {
|
||||
padding: 0.9rem;
|
||||
padding-top: 1.9rem;
|
||||
padding-bottom: 1.9rem;
|
||||
}
|
||||
|
||||
#hamb {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#locales {
|
||||
position: relative;
|
||||
margin-right: 1.8rem;
|
||||
}
|
||||
|
||||
#locales-img {
|
||||
top: 0.9rem;
|
||||
}
|
||||
}
|
55
Server/app/svelte/public/css/profile.css
Normal file
55
Server/app/svelte/public/css/profile.css
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
#notifications-div>button {
|
||||
cursor: pointer;
|
||||
margin-left: 0.341rem;
|
||||
}
|
||||
|
||||
#notifications-div>:nth-child(1) {
|
||||
width: 1.706rem;
|
||||
height: 1.706rem;
|
||||
background-color: #ccc;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#notifications-div>:nth-child(2) {
|
||||
width: 2.047rem;
|
||||
height: 2.047rem;
|
||||
background-color: #ccc;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#notifications-div>:nth-child(3) {
|
||||
width: 2.389rem;
|
||||
height: 2.389rem;
|
||||
background-color: #ccc;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#notifications-div>button>div {
|
||||
cursor: pointer;
|
||||
margin: auto;
|
||||
background-color: white;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#notifications-div>:nth-child(1)>div {
|
||||
width: 1.228rem;
|
||||
height: 1.228rem;
|
||||
}
|
||||
|
||||
#notifications-div>:nth-child(2)>div {
|
||||
width: 1.57rem;
|
||||
height: 1.57rem;
|
||||
}
|
||||
|
||||
#notifications-div>:nth-child(3)>div {
|
||||
width: 1.843rem;
|
||||
height: 1.843rem;
|
||||
}
|
||||
|
||||
*/
|
@@ -1,76 +0,0 @@
|
||||
|
||||
export let communes = [
|
||||
{
|
||||
location: [["Canada","Montreal"],[45.55541047232767, -73.42859611607271]],
|
||||
status: "forming",
|
||||
members: 2,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Denmark"],[55.915625218626275, 9.673445220831253]],
|
||||
status: "forming",
|
||||
members: 1,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Estonia","KohtlaJarve"],[59.409521829709504, 27.288415912535914]],
|
||||
status: "forming",
|
||||
members: 2,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Latvia"],[56.934159375258055, 25.269099001330265]],
|
||||
status: "forming",
|
||||
members: 1,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
}
|
||||
]
|
||||
|
||||
export let communesByCountry = {}
|
||||
for (let c of communes) {
|
||||
let country = c.location[0][0]
|
||||
if (country in communesByCountry) {
|
||||
communesByCountry[country].push(c)
|
||||
}
|
||||
else {
|
||||
communesByCountry[country] = [c]
|
||||
}
|
||||
}
|
||||
|
||||
export let communesMarkersLayer = L.layerGroup()
|
||||
|
||||
export function addMarkersCommunes(map,content) {
|
||||
for (let g of communes) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Commune"]+"</b><br>"
|
||||
for (let field in g) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact[0] + "' target='_blank' rel=noreferrer>" + content[g.contact[1]]+ "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = g[field][0]
|
||||
let locationString = location.map(x => content[x]).join(", ")
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = g[field][1]
|
||||
}
|
||||
else if (field=="status") {
|
||||
text += fieldText + content[g[field]] + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
var markerIcon = new L.Icon({
|
||||
iconUrl: 'https://www.libsoc.org/img/common/markers/marker-red.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
let marker = L.marker(coordinates, {icon: markerIcon})
|
||||
marker.addTo(communesMarkersLayer).bindPopup(text)
|
||||
}
|
||||
communesMarkersLayer.addTo(map)
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
export let coops = [
|
||||
{
|
||||
logo: "chiron_logo",
|
||||
name: "Chiron Health",
|
||||
location: [["Estonia","KohtlaJarve"],[59.41038769769602, 27.287802936242034]],
|
||||
market: "wellnessAndHealth",
|
||||
workers: 2,
|
||||
status: "inDevelopment",
|
||||
website: [ "https://www.chrn.health/", "chrn.health"],
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"],
|
||||
description: "descriptionChironHealth"
|
||||
},
|
||||
{
|
||||
logo: "kuusk_logo",
|
||||
name: "Kuusk",
|
||||
location: [["Estonia","KohtlaJarve"],[59.399947051803004, 27.277159931677055]],
|
||||
market: "herbalTeas",
|
||||
workers: 1,
|
||||
status: "inDevelopment",
|
||||
website: "-",
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"],
|
||||
description: "kuuskDescription"
|
||||
}
|
||||
]
|
||||
|
||||
export let coopsByCountry = {}
|
||||
for (let g of coops) {
|
||||
let country = g.location[0][0]
|
||||
if (country in coopsByCountry) {
|
||||
coopsByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
coopsByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
|
||||
export let coopsMarkersLayer = L.layerGroup()
|
||||
|
||||
export function addMarkersCoops(map,content) {
|
||||
for (let g of coops) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Cooperative"]+"</b><br>"
|
||||
for (let field in g) {
|
||||
let fieldText
|
||||
if (field!="logo") {
|
||||
fieldText = content[field] + ": "
|
||||
}
|
||||
if (field=="logo") {
|
||||
text += "<picture><source srcset=" + "/img/coops/" + g.logo + ".webp><source srcset='/img/coops/" + g.logo + ".png'><img alt='logo' style='position: relative; max-height: 5rem; max-width: 100%; margin: auto;'></picture>" + "<br>"
|
||||
}
|
||||
else if (field=="contact") {
|
||||
text += fieldText + "<a href='https://www." + g.contact[0] + "' target='_blank' rel=noreferrer>" + content[g.contact[1]] + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="website") {
|
||||
text += fieldText + "<a href='" + g.website[0] + "' target='_blank' rel=noreferrer>" + g.website[1] + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = g[field][0]
|
||||
let locationString = location.map(x => content[x]).join(", ")
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = g[field][1]
|
||||
}
|
||||
else if (field=="market" || field=="status" || field=="description") {
|
||||
text += fieldText + content[g[field]] + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
L.marker(coordinates).addTo(coopsMarkersLayer).bindPopup(text)
|
||||
}
|
||||
coopsMarkersLayer.addTo(map)
|
||||
}
|
@@ -1,145 +0,0 @@
|
||||
export let groups = [
|
||||
{
|
||||
location: [["Canada","Halifax"],[44.65166135030067, -63.59289968306866]],
|
||||
members: 2,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Denmark","Copenhagen"],[55.6840661150132, 12.557133959514688]],
|
||||
members: 1,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Denmark","Kolding"], [55.49261908652738, 9.470268969851743]],
|
||||
members: 1,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Estonia","KohtlaJarve"], [59.40629447076191, 27.280605339416322]],
|
||||
members: 3,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Germany","Wiesbaden"], [50.07459620869791, 8.234984059337897]],
|
||||
members: 1,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Greece","Athens"], [37.94877252621736, 23.677622972996158]],
|
||||
members: 1,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Ireland"], [53.280192832733576, -7.688103518964818]],
|
||||
members: 6,
|
||||
contact: ["https://discord.gg/4BUau4AZre","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["Latvia","Riga"], [56.94748425529816, 24.003027136431925]],
|
||||
members: 2,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
},
|
||||
{
|
||||
location: [["USA","Florida"], [26.945024427155868, -81.22162645059898]],
|
||||
members: 2,
|
||||
contact: ["https://discord.gg/Qk8KUk787z","DiscordInviteLink"]
|
||||
}
|
||||
]
|
||||
|
||||
export let groupsByCountry = {}
|
||||
for (let g of groups) {
|
||||
let country = g.location[0][0]
|
||||
if (country in groupsByCountry) {
|
||||
groupsByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
groupsByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
|
||||
export let groupsMarkersLayer = L.layerGroup()
|
||||
let groupsMarkersLayerOut = L.layerGroup()
|
||||
let groupsMarkersLayerIn = L.layerGroup()
|
||||
|
||||
let contactGeneral =["https://discord.gg/4BUau4AZre","DiscordInviteLink"]
|
||||
|
||||
function addMarkersToLayer(g,layer,content) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field in g) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact[0] + "' target='_blank' rel=noreferrer>" + content[g.contact[1]] + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = g[field][0]
|
||||
let locationString = location.map(x => content[x]).join(", ")
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = g[field][1]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
var markerIcon = new L.Icon({
|
||||
iconUrl: 'https://www.libsoc.org/img/common/markers/marker-green.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
})
|
||||
let marker = L.marker(coordinates, {icon: markerIcon})
|
||||
marker.addTo(layer).bindPopup(text)
|
||||
}
|
||||
|
||||
export function addMarkersGroups(map,content) {
|
||||
for (let g of groups) {
|
||||
addMarkersToLayer(g,groupsMarkersLayerIn,content)
|
||||
}
|
||||
for (let gs of Object.values(groupsByCountry)) {
|
||||
if (gs.length==1) {
|
||||
let g = {...gs[0]}
|
||||
g.location[0][1] = ""
|
||||
addMarkersToLayer(g,groupsMarkersLayerOut,content)
|
||||
}
|
||||
else {
|
||||
let locationName = [gs[0].location[0][0]]
|
||||
let locationCoordinates = [0,0]
|
||||
let members = 0
|
||||
let contact = gs[0].contact
|
||||
for (let g of gs) {
|
||||
locationCoordinates[0] += g.location[1][0]
|
||||
locationCoordinates[1] += g.location[1][1]
|
||||
members += g.members
|
||||
if (g.contact[0]!=gs[0].contact[0]) {
|
||||
contact = contactGeneral
|
||||
}
|
||||
}
|
||||
locationCoordinates[0] = locationCoordinates[0]/gs.length
|
||||
locationCoordinates[1] = locationCoordinates[1]/gs.length
|
||||
let gNew = {
|
||||
location: [locationName,locationCoordinates],
|
||||
members: members,
|
||||
contact: contact
|
||||
}
|
||||
|
||||
addMarkersToLayer(gNew,groupsMarkersLayerOut,content)
|
||||
}
|
||||
}
|
||||
groupsMarkersLayerOut.addTo(groupsMarkersLayer)
|
||||
groupsMarkersLayer.addTo(map)
|
||||
map.on("zoomend", () => onZoomEnd(map))
|
||||
}
|
||||
|
||||
function onZoomEnd(map) {
|
||||
let zoomLevel = map.getZoom()
|
||||
if (zoomLevel==3) {
|
||||
groupsMarkersLayer.removeLayer(groupsMarkersLayerIn)
|
||||
groupsMarkersLayerOut.addTo(groupsMarkersLayer)
|
||||
}
|
||||
else if (zoomLevel==4) {
|
||||
groupsMarkersLayer.removeLayer(groupsMarkersLayerOut)
|
||||
groupsMarkersLayerIn.addTo(groupsMarkersLayer)
|
||||
}
|
||||
}
|
163
Server/app/svelte/public/js/libraries/authTools.js
Normal file
163
Server/app/svelte/public/js/libraries/authTools.js
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
import {getData, sendData} from "/js/libraries/serverTools.js"
|
||||
|
||||
export function getUser(user,loaded,callbackOuter) {
|
||||
let callback = function(response) {
|
||||
Object.assign(user,JSON.parse(response))
|
||||
if(callbackOuter!=undefined) {
|
||||
callbackOuter()
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/xx/get-user",callback)
|
||||
}
|
||||
|
||||
export function changeUser(name,value,user) {
|
||||
if (user[name]!=value && user[name]!=undefined) {
|
||||
user[name] = value
|
||||
let data = new Object();
|
||||
data[name] = value
|
||||
sendData("/xx/change-user",data)
|
||||
}
|
||||
}
|
||||
|
||||
export function changePasswordVisibility(button) {
|
||||
let input = button.previousElementSibling
|
||||
let type = input.type
|
||||
if (type=="text") {
|
||||
input.type = "password";
|
||||
button.style.opacity = 0.25
|
||||
}
|
||||
else {
|
||||
input.type = "text";
|
||||
button.style.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
export function checkEmail(email,msg) {
|
||||
if (email.includes("@")) {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
msg.innerHTML = "must contain '@'"
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function checkPassword(password,msg) {
|
||||
let passwordLength = password.length
|
||||
if (passwordLength<8) {
|
||||
msg.innerHTML = "must be 8 characters"
|
||||
return false
|
||||
}
|
||||
let numNumbers = password.match(/\d/g)?.length || 0;
|
||||
if (numNumbers<1) {
|
||||
msg.innerHTML = "mush have digits"
|
||||
return false
|
||||
}
|
||||
let numLetters = password.match(/\D/g)?.length || 0;
|
||||
if (numLetters<2) {
|
||||
msg.innerHTML = "must have letters"
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function redirectLogged() {
|
||||
let callback = function(responseText) {
|
||||
let response = JSON.parse(responseText)
|
||||
if (response) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}
|
||||
getData("/xx/check-login",callback)
|
||||
}
|
||||
|
||||
export function redirectNotLogged() {
|
||||
let callback = function(responseText) {
|
||||
let response = JSON.parse(responseText)
|
||||
if (!response) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}
|
||||
getData("/xx/check-login",callback)
|
||||
}
|
||||
|
||||
// Redirect to the landing page
|
||||
export function toLandingPage(response) {
|
||||
if (response!=0) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to the dashboard page
|
||||
export function toDashboard() {
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
// Process log in
|
||||
export function processLoginResponse(response,msgs,remember) {
|
||||
if (response==0) {
|
||||
msgs.email.innerHTML = "not found"
|
||||
}
|
||||
else if (response==1) {
|
||||
msgs.password.innerHTML = "is wrong"
|
||||
}
|
||||
else {
|
||||
if (remember) {
|
||||
let date = new Date()
|
||||
date.setMonth(date.getMonth()+1)
|
||||
date = date.toUTCString()
|
||||
document.cookie = "__genierememberme=; expires=" + date + "; path=/;SameSite=Lax";
|
||||
}
|
||||
toDashboard()
|
||||
}
|
||||
}
|
||||
|
||||
// Log in
|
||||
export function login(msgs,inputs) {
|
||||
msgs.email.innerHTML = ""
|
||||
msgs.password.innerHTML = ""
|
||||
let data = {email: inputs.email.value, password: inputs.password.value, remember: inputs.remember.checked}
|
||||
sendData('/xx/login-post', data, (response) => processLoginResponse(response,msgs,inputs.remember.checked))
|
||||
}
|
||||
|
||||
// Process sign in
|
||||
function processSignupResponse(response,msgs) {
|
||||
if (response) {
|
||||
toDashboard()
|
||||
}
|
||||
else {
|
||||
msgs.email.innerHTML = "already exists"
|
||||
}
|
||||
}
|
||||
|
||||
// Sign up
|
||||
export function signup(msgs,inputs) {
|
||||
msgs.email.innerHTML = ""
|
||||
let email = inputs.email.value
|
||||
let password = inputs.password.value
|
||||
if (checkEmail(email,msgs.email)==false) {
|
||||
return
|
||||
}
|
||||
if (checkPassword(password,msgs.password)==false) {
|
||||
return
|
||||
}
|
||||
let data = {email: email, password: password}
|
||||
sendData('/xx/signup-post', data, (response) => processSignupResponse(response,msgs))
|
||||
}
|
||||
|
||||
export function confirmEmail(msg,code,callback) {
|
||||
msg.innerHTML = ""
|
||||
sendData('xx/confirm-email',code,callback)
|
||||
}
|
||||
|
||||
// Log out
|
||||
export function logout() {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open( "GET", "/logout", false ); // false for synchronous request
|
||||
xmlHttp.send( null );
|
||||
window.location.href = "/";
|
||||
}
|
102
Server/app/svelte/public/js/libraries/mapTools.js
Normal file
102
Server/app/svelte/public/js/libraries/mapTools.js
Normal file
@@ -0,0 +1,102 @@
|
||||
let contactGeneral =["https://discord.gg/4BUau4AZre","DiscordInviteLink"]
|
||||
|
||||
export function translate(content, x) {
|
||||
let out = content[x]
|
||||
if (out==undefined) {
|
||||
return x
|
||||
}
|
||||
else {
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
function addMarkersToLayer(g,layer,content,locale,addPinContent,markerColor,options) {
|
||||
let {text,coordinates} = addPinContent(g,content,locale)
|
||||
var markerIcon = new L.Icon({
|
||||
iconUrl: 'https://www.libsoc.org/img/common/markers/marker-' + markerColor + '.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
})
|
||||
let marker = L.marker(coordinates, {icon: markerIcon})
|
||||
marker.id = g.id
|
||||
marker.members = g.members
|
||||
marker.contact = g.contact
|
||||
marker.addTo(layer).bindPopup(text)
|
||||
|
||||
if (options.pinCallback!=undefined) {
|
||||
marker.on('click', (event) => options.pinCallback(marker,event))
|
||||
}
|
||||
}
|
||||
|
||||
export function addMarkersEntries(entries,entriesByCountry,map,content,locale,addPinContent,markerColor,options) {
|
||||
let entriesMarkersLayer = L.layerGroup()
|
||||
let entriesMarkersLayerOut = L.layerGroup()
|
||||
let entriesMarkersLayerIn = L.layerGroup()
|
||||
for (let g of entries) {
|
||||
if (g.country!="Online" && g.country!="Worldwide") {
|
||||
addMarkersToLayer(g,entriesMarkersLayerIn,content,locale,addPinContent,markerColor,options)
|
||||
}
|
||||
}
|
||||
if (options.enableCountryGrouping) {
|
||||
for (let gs of Object.values(entriesByCountry)) {
|
||||
if (gs.length==1) {
|
||||
let g = {...gs[0]}
|
||||
g.country = [g.country]
|
||||
if (g.country!="Online" && g.country!="Worldwide") {
|
||||
addMarkersToLayer(g,entriesMarkersLayerOut,content,locale,addPinContent,markerColor,options)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gs[0].country!="Online" && gs[0].country!="Worldwide") {
|
||||
let locationName = gs[0].country
|
||||
let locationCoordinates = [0,0]
|
||||
let members = 0
|
||||
let contact = gs[0].contact
|
||||
for (let g of gs) {
|
||||
locationCoordinates[0] += g.latitude
|
||||
locationCoordinates[1] += g.longitude
|
||||
members += g.members
|
||||
if (g.contact[0]!=gs[0].contact[0]) {
|
||||
contact = contactGeneral
|
||||
}
|
||||
}
|
||||
locationCoordinates[0] = locationCoordinates[0]/gs.length
|
||||
locationCoordinates[1] = locationCoordinates[1]/gs.length
|
||||
let gNew = {
|
||||
country: locationName,
|
||||
latitude: locationCoordinates[0],
|
||||
longitude: locationCoordinates[1],
|
||||
members: members,
|
||||
contact: contact
|
||||
}
|
||||
addMarkersToLayer(gNew,entriesMarkersLayerOut,content,locale,addPinContent,markerColor,options)
|
||||
}
|
||||
}
|
||||
}
|
||||
entriesMarkersLayerOut.addTo(entriesMarkersLayer)
|
||||
map.on("zoomend", () => onZoomEnd(map,entriesMarkersLayer,entriesMarkersLayerOut,entriesMarkersLayerIn))
|
||||
}
|
||||
else {
|
||||
entriesMarkersLayerIn.addTo(entriesMarkersLayer)
|
||||
}
|
||||
entriesMarkersLayer.addTo(map)
|
||||
|
||||
return entriesMarkersLayer
|
||||
}
|
||||
|
||||
function onZoomEnd(map,entriesMarkersLayer,entriesMarkersLayerOut,entriesMarkersLayerIn) {
|
||||
let zoomLevel = map.getZoom()
|
||||
if (zoomLevel==3) {
|
||||
entriesMarkersLayer.removeLayer(entriesMarkersLayerIn)
|
||||
entriesMarkersLayerOut.addTo(entriesMarkersLayer)
|
||||
}
|
||||
else if (zoomLevel==4) {
|
||||
entriesMarkersLayer.removeLayer(entriesMarkersLayerOut)
|
||||
entriesMarkersLayerIn.addTo(entriesMarkersLayer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,17 +1,17 @@
|
||||
|
||||
// Get data from server
|
||||
export function getData(path,callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType("application/json");
|
||||
xhr.open('GET', path, true);
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType("application/json")
|
||||
xhr.open('GET', path, true)
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4 && xhr.status == "200") {
|
||||
if (callback !== undefined) {
|
||||
callback(xhr.responseText);
|
||||
callback(xhr.responseText)
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
xhr.send()
|
||||
}
|
||||
|
||||
// Parse JSON from given path into a given variable under a given key
|
||||
@@ -35,6 +35,8 @@ export function sendData(route,data,callback) {
|
||||
callback(xhr.responseText)
|
||||
}
|
||||
} else {
|
||||
callback(false)
|
||||
console.log("Request gave an error")
|
||||
// Oh no! There has been an error with the request!
|
||||
}
|
||||
}
|
||||
@@ -55,6 +57,8 @@ export function sendText(route,data,callback) {
|
||||
callback(xhr.responseText)
|
||||
}
|
||||
} else {
|
||||
callback(false)
|
||||
console.log("Request gave an error")
|
||||
// Oh no! There has been an error with the request!
|
||||
}
|
||||
}
|
||||
|
178
Server/app/svelte/public/js/mapFuncs.js
Normal file
178
Server/app/svelte/public/js/mapFuncs.js
Normal file
@@ -0,0 +1,178 @@
|
||||
import { translate } from "/js/libraries/mapTools.js"
|
||||
|
||||
export function addGroupPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field of ["location","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
export function addCommunePinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Commune"]+"</b><br>"
|
||||
for (let field of ["location","status","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else if (field=="status") {
|
||||
text += fieldText + content[g[field]] + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
export function addCoopPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Cooperative"]+"</b><br>"
|
||||
for (let field of ["logo","name","location","workers","status","website","contact","description"]) {
|
||||
let fieldText
|
||||
if (field!="logo") {
|
||||
fieldText = content[field] + ": "
|
||||
}
|
||||
if (field=="logo") {
|
||||
text += "<picture><source srcset=" + "/img/coops/" + g.logo + ".webp><source srcset='/img/coops/" + g.logo + ".png'><img alt='logo' style='position: relative; max-height: 5rem; max-width: 100%; margin: auto;'></picture>" + "<br>"
|
||||
}
|
||||
else if (field=="contact") {
|
||||
text += fieldText + "<a href='https://www." + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="website") {
|
||||
text += fieldText + "<a href='" + g.website + "' target='_blank' rel=noreferrer>" + g.website + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else if (field=="market" || field=="status" || field=="description") {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
export function addPartyPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Cooperative"]+"</b><br>"
|
||||
for (let field of ["logo","name","location","website","contact","description"]) {
|
||||
let fieldText
|
||||
if (field!="logo") {
|
||||
fieldText = content[field] + ": "
|
||||
}
|
||||
if (field=="logo") {
|
||||
text += "<picture><source srcset=" + "/img/parties/" + g.logo + ".webp><source srcset='/img/parties/" + g.logo + ".png'><img alt='logo' style='position: relative; max-height: 5rem; max-width: 100%; margin: auto;'></picture>" + "<br>"
|
||||
}
|
||||
else if (field=="link") {
|
||||
text += fieldText + "<a href='" + g.link + "' target='_blank' rel=noreferrer>" + g.link + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="website") {
|
||||
text += fieldText + "<a href='" + g.website + "' target='_blank' rel=noreferrer>" + g.website + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else if (field=="description") {
|
||||
text += fieldText + content[g[field]] + "<br>"
|
||||
}
|
||||
else if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>" + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
export function addPartnersPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Partner"]+"</b><br>"
|
||||
for (let field of ["logo","name","location","website","contact","description"]) {
|
||||
let fieldText
|
||||
if (field!="logo") {
|
||||
fieldText = content[field] + ": "
|
||||
}
|
||||
if (field=="logo") {
|
||||
text += "<picture><source srcset=" + "/img/coops/" + g.logo + ".webp><source srcset='/img/coops/" + g.logo + ".png'><img alt='logo' style='position: relative; max-height: 5rem; max-width: 100%; margin: auto;'></picture>" + "<br>"
|
||||
}
|
||||
else if (field=="contact") {
|
||||
text += fieldText + "<a href='https://www." + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="website") {
|
||||
text += fieldText + "<a href='" + g.website + "' target='_blank' rel=noreferrer>" + g.website + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else if (field=="description") {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
@@ -1,68 +0,0 @@
|
||||
export let parties = [
|
||||
{
|
||||
logo: "roots",
|
||||
name: "Roots",
|
||||
location: ["Ireland",[52.98479517270413, -7.649233227534782]],
|
||||
//members: 6,
|
||||
link: "https://discord.gg/pSTMacJZsK",
|
||||
description: "descriptionRoots"
|
||||
}
|
||||
]
|
||||
|
||||
export let partiesByCountry = {}
|
||||
for (let g of parties) {
|
||||
let country = g.location[0]
|
||||
if (country in partiesByCountry) {
|
||||
partiesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
partiesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
|
||||
export let partiesMarkersLayer = L.layerGroup()
|
||||
|
||||
export function addMarkersParties(map,content) {
|
||||
for (let g of parties) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Party"]+"</b><br>"
|
||||
for (let field in g) {
|
||||
let fieldText
|
||||
if (field!="logo") {
|
||||
fieldText = content[field] + ": "
|
||||
}
|
||||
if (field=="logo") {
|
||||
text += "<picture><source srcset=" + "/img/parties/" + g.logo + ".webp><source srcset='/img/parties/" + g.logo + ".png'><img alt='logo' style='position: relative; max-height: 5rem; max-width: 100%; margin: auto;'></picture>" + "<br>"
|
||||
}
|
||||
else if (field=="link") {
|
||||
text += fieldText + "<a href='" + g.link + "' target='_blank' rel=noreferrer>" + g.link + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="website") {
|
||||
text += fieldText + "<a href='" + g.website + "' target='_blank' rel=noreferrer>" + g.website + "</a>" + "<br>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = g[field][0]
|
||||
let locationString = location
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = g[field][1]
|
||||
}
|
||||
else if (field=="description") {
|
||||
text += fieldText + content[g[field]] + "<br>"
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
var markerIcon = new L.Icon({
|
||||
iconUrl: 'https://www.libsoc.org/img/common/markers/marker-gold.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
let marker = L.marker(coordinates, {icon: markerIcon})
|
||||
marker.addTo(partiesMarkersLayer).bindPopup(text)
|
||||
}
|
||||
partiesMarkersLayer.addTo(map)
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
export let partners = [
|
||||
{
|
||||
name: "Gaia's Fall",
|
||||
type: "typeGaiasFall",
|
||||
location: [["Online"],[0,0]],
|
||||
link: "https://discord.libsoc.org/invite/",
|
||||
description: "descriptionGaiasFall",
|
||||
logo: "gaias_fall"
|
||||
}
|
||||
]
|
||||
|
||||
export let partnersByCountry = {}
|
||||
for (let g of partners) {
|
||||
let country = g.location[0][0]
|
||||
if (country in partnersByCountry) {
|
||||
partnersByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
partnersByCountry[country] = [g]
|
||||
}
|
||||
}
|
0
Server/app/svelte/public/locales/countries.csv
Normal file
0
Server/app/svelte/public/locales/countries.csv
Normal file
|
@@ -10,5 +10,6 @@
|
||||
"forming": "forming",
|
||||
"WhatsAppInviteLink": "WhatsApp invite link",
|
||||
"DiscordInviteLink": "Discord invite link",
|
||||
"Commune": "Commune"
|
||||
"Commune": "Commune",
|
||||
"map-prompt": "Want to appear on our map? Contact us!"
|
||||
}
|
@@ -18,6 +18,7 @@
|
||||
"descriptionChironHealth": "Chiron Health is a health platform providing courses and services on the topics of nutrition, exercise, sleep and mental wellbeing.",
|
||||
"herbalTeas": "herbal teas",
|
||||
"kuuskDescription": "Kuusk is an online store that sells herbal teas from exclusively local wild plants, as well as an online gathering course.",
|
||||
"Cooperative": "Cooperative"
|
||||
"Cooperative": "Cooperative",
|
||||
"map-prompt": "Want to appear on our map? Contact us!"
|
||||
|
||||
}
|
@@ -1,21 +1,3 @@
|
||||
{
|
||||
"map-prompt": "Want to appear on our map? Contact us!",
|
||||
"Online": "Online",
|
||||
"Denmark": "Denmark",
|
||||
"Estonia": "Estonia",
|
||||
"Greece": "Greece",
|
||||
"Latvia": "Latvia",
|
||||
"Canada": "Canada",
|
||||
"Germany": "Germany",
|
||||
"Copenhagen": "Copenhagen",
|
||||
"Ireland": "Ireland",
|
||||
"USA": "USA",
|
||||
"Montreal": "Montreal",
|
||||
"Kolding": "Kolding",
|
||||
"KohtlaJarve": "Kohtla-Järve",
|
||||
"Athens": "Athens",
|
||||
"Riga": "Riga",
|
||||
"Halifax": "Halifax",
|
||||
"Wiesbaden": "Wiesbaden",
|
||||
"Florida": "Florida"
|
||||
|
||||
}
|
@@ -8,5 +8,6 @@
|
||||
"contact": "Contact",
|
||||
"DiscordInviteLink": "Discord invite link",
|
||||
"WhatsAppInviteLink": "WhatsApp invite link",
|
||||
"Group": "Group"
|
||||
"Group": "Group",
|
||||
"map-prompt": "Want to appear on our map? Contact us!"
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"top": "Our organization is a decentralized federation build upon the principle of free association. It consists of many groups of people united around a cause of bringing down exploitative politico-economic systems. We aim to replace them with libertarian socialist systems, with the goal of creating an equitable, democratic and sustainable world by stopping exploitation of humans and nature.",
|
||||
"top": "Our organization is a decentralized federation build upon the principle of free association. It consists of many groups of people united around a cause of bringing down exploitative politico-economic systems. We aim to replace them with libertarian socialist systems based on decentralization, direct democracy and worker-ownership of the means of production with the goal of creating an equitable, democratic and sustainable world by stopping exploitation of humans and nature.",
|
||||
"groupsTitle": "GROUPS",
|
||||
"groupsText": "We organize groups for the purposes of education, advocacy, and mutual aid. Our objective is to demonstrate how the current politico-economic systems detrimentally impact our well-being, present alternative approaches, and engage in mutual aid to alleviate the challenges of living under capitalism.",
|
||||
"communesTitle": "COMMUNES",
|
||||
|
@@ -87,7 +87,7 @@ Under capitalism, atomization occurs due to several interconnected factors:
|
||||
<b>Individualism and Competition:</b> Capitalism places a strong emphasis on individualism and competition, promoting the pursuit of self-interest and personal gain. This focus on individual success leads to a sense of isolation as people prioritize their own needs and goals over collective well-being. The constant competition for resources and opportunities further fragments society, fostering a mindset of "every person for themselves."
|
||||
<b>Market Forces and Commodification:</b> In capitalist systems, nearly every aspect of life is subjected to market forces. Commodification occurs when goods, services, and even human relationships are reduced to exchangeable commodities. This commodification mentality erodes social bonds and interpersonal relationships, as people are encouraged to view others primarily as potential buyers or sellers rather than as fellow human beings.
|
||||
<b>Consumer Culture:</b> Capitalism promotes a consumer culture that emphasizes the pursuit of material possessions and immediate gratification. This culture fosters a sense of individualistic consumption, where personal happiness and identity are tied to the acquisition of goods and services. The constant pursuit of consumerism isolates individuals as they prioritize material accumulation over social connections and shared experiences.
|
||||
</b>Social Inequalities:</b> Capitalist systems perpetuate social inequalities based on wealth, class, race, and gender. These inequalities result in marginalization, discrimination, and exclusion of certain groups from full participation in society. Such divisions further contribute to social atomization by creating barriers to collective action and fostering distrust among different social groups.
|
||||
<b>Social Inequalities:</b> Capitalist systems perpetuate social inequalities based on wealth, class, race, and gender. These inequalities result in marginalization, discrimination, and exclusion of certain groups from full participation in society. Such divisions further contribute to social atomization by creating barriers to collective action and fostering distrust among different social groups.
|
||||
Atomization under capitalism serves as a powerful tool that prevents us from organizing and challenging the systemic inequalities and injustices inherent in the capitalist system.
|
||||
### Externalities
|
||||
Externalities refer to the unintended consequences of economic activities that are not reflected in the prices of goods and services. These consequences can be positive or negative and are often borne by individuals or communities who are not directly involved in the economic transactions.
|
||||
|
@@ -2,10 +2,12 @@
|
||||
"orgName": "Libertarian socialists",
|
||||
"manifesto": "Manifesto",
|
||||
"joinUs": "Join us",
|
||||
"aboutUs": "About us",
|
||||
"initiatives": "Initiatives",
|
||||
"groups": "Groups",
|
||||
"communes": "Communes",
|
||||
"cooperatives": "Cooperatives",
|
||||
"parties": "Parties",
|
||||
"partners": "Partners"
|
||||
"partners": "Partners",
|
||||
"login": "Login",
|
||||
"profile": "Profile"
|
||||
}
|
@@ -5,8 +5,10 @@
|
||||
"name": "Name",
|
||||
"location": "Location",
|
||||
"members": "Members",
|
||||
"link": "Link",
|
||||
"website": "Website",
|
||||
"contact": "Contact",
|
||||
"description": "Description",
|
||||
"descriptionRoots": "We are a left libertarian organization based in Ireland. We have a focus on rebuilding the country's infrastructure, sense of governance, housing, agricultural industries and youth affairs.",
|
||||
"Party": "Party"
|
||||
"Party": "Party",
|
||||
"map-prompt": "Want to appear on our map? Contact us!"
|
||||
}
|
@@ -5,10 +5,11 @@
|
||||
"subheading2": "Online",
|
||||
"name": "Name",
|
||||
"location": "Location",
|
||||
"type": "Type",
|
||||
"link": "Link",
|
||||
"website": "Website",
|
||||
"contact": "Contact",
|
||||
"description": "Description",
|
||||
"typeGaiasFall": "a place for discussions",
|
||||
"descriptionGaiasFall": "Gaia's Fall is a server that promotes Solarpunk ideals, environmentalism, anarchism, and anti-capitalism. We encourage civil debates, discussions of theories and possibilities, and the creation of communities focused on shaping a better world."
|
||||
"descriptionGaiasFall": "Gaia's Fall is a server that promotes Solarpunk ideals, environmentalism, anarchism, and anti-capitalism. We encourage civil debates, discussions of theories and possibilities, and the creation of communities focused on shaping a better world.",
|
||||
"map-prompt": "Want to appear on our map? Contact us!"
|
||||
|
||||
}
|
@@ -9,5 +9,6 @@
|
||||
"forming": "формируется",
|
||||
"WhatsAppInviteLink": "WhatsApp ссылка",
|
||||
"DiscordInviteLink": "Discord ссылка",
|
||||
"Commune": "Коммуна"
|
||||
"Commune": "Коммуна",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -18,5 +18,6 @@
|
||||
"descriptionChironHealth": "Chiron Health — это платформа о здоровье, предлагающая курсы и услуги по вопросам питания, физических упражнений, сна и психического благополучия",
|
||||
"herbalTeas": "травяные чаи",
|
||||
"kuuskDescription": "Kuusk — интернет-магазин, в котором продаются травяные чаи исключительно из местных дикорастущих растений, а также онлайн-курс по собирательству.",
|
||||
"Cooperative": "Кооператив"
|
||||
"Cooperative": "Кооператив",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -1,21 +1,3 @@
|
||||
{
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!",
|
||||
"Online": "Онлайн",
|
||||
"Denmark": "Дания",
|
||||
"Estonia": "Эстония",
|
||||
"Greece": "Греция",
|
||||
"Latvia": "Латвия",
|
||||
"Canada": "Канада",
|
||||
"Ireland": "Ирландия",
|
||||
"Germany": "Германия",
|
||||
"USA": "CША",
|
||||
"Copenhagen": "Копенгаген",
|
||||
"Kolding": "Колдинг",
|
||||
"KohtlaJarve": "Кохтла-Ярве",
|
||||
"Athens": "Афины",
|
||||
"Riga": "Рига",
|
||||
"Halifax": "Галифакс",
|
||||
"Montreal": "Монреаль",
|
||||
"Wiesbaden": "Висбаден",
|
||||
"Florida": "Флорида"
|
||||
|
||||
}
|
@@ -7,5 +7,6 @@
|
||||
"contact": "Контакт",
|
||||
"DiscordInviteLink": "Discord ссылка",
|
||||
"WhatsAppInviteLink": "WhatsApp ссылка",
|
||||
"Group": "Группа"
|
||||
"Group": "Группа",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -30,5 +30,6 @@
|
||||
"market": "Рынок",
|
||||
"workers": "Работники",
|
||||
"description": "Описание",
|
||||
"website": "Вебсайт"
|
||||
"website": "Вебсайт",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"top": "Наша организация представляет собой децентрализованную федерацию, основанную на принципе свободного объединения. Она состоит из многих групп людей, объединенных вокруг цели уничтожения эксплуататорских политико-экономических систем. Мы стремимся заменить их либертарными социалистическими системами с целью создания справедливого, демократического и устойчивого мира путем прекращения эксплуатации людей и природы.",
|
||||
"top": "Наша организация представляет собой децентрализованную федерацию, основанную на принципе свободного объединения. Она состоит из многих групп людей, объединенных вокруг цели свержения эксплуататорских политико-экономических систем. Мы стремимся заменить их либертарными социалистическими системами, основанными на децентрализации, прямой демократии и собственности рабочих на средства производства, с целью создания справедливого, демократического и устойчивого мира путем прекращения эксплуатации людей и природы.",
|
||||
"groupsTitle": "ГРУППЫ",
|
||||
"groupsText": "Мы организуем группы в целях образования, защиты наших интересов и взаимопомощи. Наша цель — продемонстрировать людям, как нынешние политико-экономические системы пагубно влияют на наше благополучие, представить им альтернативные варианты и заниматься взаимопомощью, чтобы сделать жизнь при капитализме легче.",
|
||||
"communesTitle": "КОММУНЫ",
|
||||
@@ -11,5 +11,6 @@
|
||||
"findUs": "Найди нас",
|
||||
"whatNow": "Что теперь?",
|
||||
"joinUs": "Присоединяйся",
|
||||
"talkWithUs": "Напиши нам"
|
||||
"talkWithUs": "Напиши нам",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
"orgName": "Либертарные социалисты",
|
||||
"manifesto": "Манифест",
|
||||
"joinUs": "Присоединяйся",
|
||||
"aboutUs": "О нас",
|
||||
"initiatives": "Инициативы",
|
||||
"groups": "Группы",
|
||||
"communes": "Коммуны",
|
||||
"cooperatives": "Кооперативы",
|
||||
|
@@ -5,9 +5,11 @@
|
||||
"name": "Имя",
|
||||
"location": "Локация",
|
||||
"members": "Участники",
|
||||
"link": "Ссылка",
|
||||
"website": "Вебсайт",
|
||||
"contact": "Контакт",
|
||||
"description": "Описание",
|
||||
"ireland": "Ирландия",
|
||||
"descriptionRoots": "Мы — левая либертарная организация, базирующаяся в Ирландии. Мы уделяем особое внимание восстановлению инфраструктуры страны, самоуправлению, жилищному строительству, сельскохозяйственной промышленности и делам молодежи.",
|
||||
"Party": "Партия"
|
||||
"Party": "Партия",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -6,8 +6,10 @@
|
||||
"name": "Название",
|
||||
"location": "Локация",
|
||||
"type": "Тип",
|
||||
"link": "Ссылка",
|
||||
"website": "Вебсайт",
|
||||
"contact": "Контакт",
|
||||
"description": "Описание",
|
||||
"typeGaiasFall": "место для общения",
|
||||
"descriptionGaiasFall": "Gaia's Fall — это сервер, который продвигает идеалы соларпанка, защиту окружающей среды, анархизм и антикапитализм. Мы поощряем гражданские дебаты, обсуждение теорий и возможностей, а также создание сообществ, сосредоточенных на формировании лучшего мира."
|
||||
"descriptionGaiasFall": "Gaia's Fall — это сервер, который продвигает идеалы соларпанка, защиту окружающей среды, анархизм и антикапитализм. Мы поощряем гражданские дебаты, обсуждение теорий и возможностей, а также создание сообществ, сосредоточенных на формировании лучшего мира.",
|
||||
"map-prompt": "Хочешь оказаться на нашей карте? Напиши нам!"
|
||||
}
|
@@ -32,7 +32,6 @@ function serve() {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const walkSync = require("walk-sync");
|
||||
const paths = walkSync("./src", {globs: ["**/*.svelte"]}).map(x => "src/"+x)
|
||||
|
||||
|
129
Server/app/svelte/src/auth/auth-component.svelte
Normal file
129
Server/app/svelte/src/auth/auth-component.svelte
Normal file
@@ -0,0 +1,129 @@
|
||||
<svelte:options tag="auth-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, setContext,getContext } from 'svelte'
|
||||
import { sendText } from "/js/libraries/serverTools.js"
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
import "/js/components/login-component.js"
|
||||
import "/js/components/signup-component.js"
|
||||
|
||||
// Main code
|
||||
AuthTools.redirectLogged()
|
||||
|
||||
let loginComponent
|
||||
let signupComponent
|
||||
|
||||
let context = {
|
||||
googleInit: false
|
||||
}
|
||||
setContext("auth",context)
|
||||
|
||||
function switchFocus(component) {
|
||||
if (component==loginComponent) {
|
||||
loginComponent.focused = true
|
||||
signupComponent.focused = false
|
||||
}
|
||||
else {
|
||||
loginComponent.focused = false
|
||||
signupComponent.focused = true
|
||||
}
|
||||
}
|
||||
|
||||
function callbackGoogle(data) {
|
||||
console.log(data)
|
||||
sendText("/signup-google",data.credential,(response) => AuthTools.processLoginResponse(response,context.msgs,context.remember.checked))
|
||||
}
|
||||
|
||||
function initGoogle() {
|
||||
if (typeof google != 'undefined') {
|
||||
google.accounts.id.initialize({
|
||||
client_id: '93612176787-sr8qjqem4e3kok4msrnj8s1illt85a9g.apps.googleusercontent.com',
|
||||
callback: callbackGoogle,
|
||||
auto_select: true,
|
||||
context: "signin"
|
||||
})
|
||||
context.googleInit = true
|
||||
}
|
||||
else {
|
||||
setTimeout(initGoogle,100)
|
||||
}
|
||||
}
|
||||
|
||||
initGoogle()
|
||||
|
||||
</script>
|
||||
|
||||
<div id="auth-group">
|
||||
<div id="auth-grid-group">
|
||||
<login-component bind:this={loginComponent} on:click={() => switchFocus(loginComponent)} on:keydown={() => ""}></login-component>
|
||||
<signup-component bind:this={signupComponent} on:click={() => switchFocus(signupComponent)} on:keydown={() => ""}></signup-component>
|
||||
</div>
|
||||
<div id="auth-or" class="pane">
|
||||
<span>OR</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
@import '/css/auth.css';
|
||||
|
||||
span {
|
||||
font-size: 1.4rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
}
|
||||
|
||||
#auth-group {
|
||||
margin: auto;
|
||||
width: auto;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
#auth-grid-group {
|
||||
display: grid;
|
||||
grid-template-columns: 30rem 30rem;
|
||||
justify-content: center;
|
||||
gap: 1.37rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#auth-or {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
top: 40%;/*40%;*/
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 5.4rem;
|
||||
height: 5.4rem;
|
||||
border-radius: 6.8rem;
|
||||
background-color: white;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
#auth-grid-group {
|
||||
display: grid;
|
||||
grid-template-columns: 30rem;
|
||||
grid-template-rows: auto auto;
|
||||
justify-content: center;
|
||||
gap: 1.37rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#auth-or {
|
||||
top: 40rem;/*46.4rem;*/
|
||||
}
|
||||
|
||||
#auth-group {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
131
Server/app/svelte/src/auth/confirmation-component.svelte
Normal file
131
Server/app/svelte/src/auth/confirmation-component.svelte
Normal file
@@ -0,0 +1,131 @@
|
||||
<svelte:options tag="confirmation-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
// Export statements
|
||||
|
||||
// Main code
|
||||
let confirmationInputs = []
|
||||
let confirmationMsg
|
||||
let confirmationButton
|
||||
|
||||
function onlyNumberKey(ind,evt) {
|
||||
// Only ASCII character in that range allowed
|
||||
var value = evt.data
|
||||
if (value in ["0","1","2","3","4","5","6","7","8","9"]) {
|
||||
if (ind<4) {
|
||||
confirmationInputs[ind+1].focus()
|
||||
}
|
||||
else {
|
||||
AuthTools.confirmEmail(confirmationMsg,getCode(),callback)
|
||||
}
|
||||
}
|
||||
else {
|
||||
confirmationInputs[ind].value = ""
|
||||
}
|
||||
}
|
||||
|
||||
function getCode() {
|
||||
let code = ""
|
||||
for (let input of confirmationInputs) {
|
||||
code += input.value
|
||||
}
|
||||
return parseInt(code)
|
||||
}
|
||||
|
||||
function callback(response) {
|
||||
if (response=="true") {
|
||||
AuthTools.toDashboard()
|
||||
}
|
||||
else {
|
||||
confirmationMsg.innerHTML = "Wrong code"
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="pane auth-pane">
|
||||
<h2 class="auth-title title-highlight">CONFIRMATION CODE</h2>
|
||||
<div id="confirmationInputs">
|
||||
<input bind:this={confirmationInputs[0]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(0,evt)}><span class="dash">-</span>
|
||||
<input bind:this={confirmationInputs[1]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(1,evt)}><span class="dash">-</span>
|
||||
<input bind:this={confirmationInputs[2]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(2,evt)}><span class="dash">-</span>
|
||||
<input bind:this={confirmationInputs[3]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(3,evt)}><span class="dash">-</span>
|
||||
<input bind:this={confirmationInputs[4]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(4,evt)}>
|
||||
</div>
|
||||
<span bind:this={confirmationMsg} id="confirmation-msg"></span>
|
||||
<button bind:this={confirmationButton} class="auth-button" on:click="{() => AuthTools.confirmEmail(confirmationMsg,getCode(),callback)}">Confirm</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
.auth-pane {
|
||||
position: relative;
|
||||
padding: 3.4rem;
|
||||
padding-top: 5.5rem;
|
||||
padding-bottom: 5.5rem;
|
||||
width: 33rem;
|
||||
height: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
position: relative;
|
||||
left: 0.7rem;
|
||||
top: 0.2rem;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
|
||||
.authConfirmationInput {
|
||||
position: relative;
|
||||
width: 3.16rem;
|
||||
font-family: var(--serif,serif);
|
||||
font-size: 3rem;
|
||||
border-radius: 0.34rem;
|
||||
margin-bottom: 0.7rem;
|
||||
text-align: center;
|
||||
padding-left: 0;
|
||||
padding-bottom: 0.3 rem;
|
||||
}
|
||||
|
||||
.dash {
|
||||
display: block;
|
||||
font-size: 3rem;
|
||||
font-family: var(--serif,serif);
|
||||
}
|
||||
|
||||
#confirmationInputs {
|
||||
margin: auto;
|
||||
display: grid;
|
||||
justify-content: space-between;
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
|
||||
.auth-button {
|
||||
margin-top: 1.4rem;
|
||||
height: 3.4rem;
|
||||
width: 100%;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.6rem;
|
||||
color: white;
|
||||
background-color: var(--pink);
|
||||
border-color: var(--pink);
|
||||
border-radius: 0.5rem;
|
||||
filter: drop-shadow(0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
}
|
||||
|
||||
#confirmation-msg {
|
||||
display: inline;
|
||||
color:red;
|
||||
}
|
||||
</style>
|
110
Server/app/svelte/src/auth/login-component.svelte
Normal file
110
Server/app/svelte/src/auth/login-component.svelte
Normal file
@@ -0,0 +1,110 @@
|
||||
<svelte:options tag="login-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
// Export statements
|
||||
export let focused = false
|
||||
|
||||
// Main code
|
||||
|
||||
let emailInput
|
||||
let passwordInput
|
||||
let inputs
|
||||
|
||||
let passwordVisibilityButton
|
||||
|
||||
let emailMsg
|
||||
let passwordMsg
|
||||
let msgs
|
||||
let rememberMe
|
||||
|
||||
let googleButton
|
||||
|
||||
let parentProps = getContext("auth")
|
||||
|
||||
function renderGoogle() {
|
||||
if (parentProps.googleInit) {
|
||||
google.accounts.id.renderButton(googleButton,{
|
||||
theme: 'outline',
|
||||
size: 'large'
|
||||
})
|
||||
let iframe = googleButton.getElementsByTagName('iframe')[0]
|
||||
iframe.style.height = "5rem"
|
||||
iframe.style.width = "5rem"
|
||||
}
|
||||
else {
|
||||
setTimeout(renderGoogle,100)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
rememberMe.checked = true
|
||||
|
||||
inputs = {email: emailInput, password: passwordInput, remember: rememberMe}
|
||||
msgs = {email: emailMsg, password: passwordMsg}
|
||||
|
||||
parentProps.msgs = msgs
|
||||
parentProps.remember = rememberMe
|
||||
parentProps.loginGoogle = googleButton
|
||||
|
||||
document.addEventListener("keypress", function(event) {
|
||||
if (event.code == "Enter") {
|
||||
if (focused) {
|
||||
AuthTools.login(msgs,inputs)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
renderGoogle()
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<div id="login-group"class="pane auth-pane">
|
||||
<h2 class="auth-title">LOG IN</h2>
|
||||
<label class="auth-label" for="emailInput">Email </label><span bind:this={emailMsg} id="email-msg"></span>
|
||||
<input bind:this={emailInput} id="emailInput" class="authEmailInput" type="email">
|
||||
<div class="password-field">
|
||||
<label class="auth-label" for="passwordInput">Password </label><span bind:this={passwordMsg} id="password-msg"></span>
|
||||
<input bind:this={passwordInput} id="passwordInput" class="authPasswordInput" type="password">
|
||||
<button bind:this={passwordVisibilityButton} class="eye-icon" on:click="{() => AuthTools.changePasswordVisibility(passwordVisibilityButton)}">
|
||||
<object type="image/svg+xml" data="/img/auth/eye_icon.svg" title="eye icon"></object>
|
||||
</button>
|
||||
</div>
|
||||
<div id="remember-me">
|
||||
<input bind:this={rememberMe} type="checkbox" id="remember-me-checkbox"><label id="remember-me-label" for="passwordInput">remember me</label>
|
||||
</div>
|
||||
<button class="auth-button" on:click="{() => AuthTools.login(msgs,inputs)}">Log in</button>
|
||||
<a id="forgot-password" href="forgot-password">Forgot password?</a>
|
||||
<!--
|
||||
<hr class="auth-line">
|
||||
<div class="auth-methods-group">
|
||||
<div id="google-btn-wrapper">
|
||||
<div bind:this={googleButton} id="google-btn"></div>
|
||||
<img src="/img/auth/google_icon.svg" id="google-logo" alt="google icon">
|
||||
</div>
|
||||
|
||||
<button on:click={openGoogleWindow}>
|
||||
<img src="img/auth/google_icon.svg" id="navbar-logo" alt="google icon">
|
||||
</button>
|
||||
<button onclick="">
|
||||
<img src="img/auth/facebook_icon.svg" id="navbar-logo" alt="facebook icon">
|
||||
</button>
|
||||
<button onclick="">
|
||||
<img src="img/auth/linkedin_icon.svg" id="navbar-logo" alt="linkedin icon">
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
@import '/css/auth.css';
|
||||
|
||||
</style>
|
243
Server/app/svelte/src/auth/signup-component.svelte
Normal file
243
Server/app/svelte/src/auth/signup-component.svelte
Normal file
@@ -0,0 +1,243 @@
|
||||
<svelte:options tag="signup-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
// Export statements
|
||||
export let focused = false
|
||||
|
||||
// Main code
|
||||
|
||||
let signupGroup
|
||||
let emailInput
|
||||
let passwordInput
|
||||
let passwordVisibilityButton
|
||||
let inputs
|
||||
let googleButton
|
||||
|
||||
let emailMsg
|
||||
let passwordMsg
|
||||
let msgs
|
||||
let rememberMe
|
||||
|
||||
let dialog
|
||||
let signUp
|
||||
let signUpField
|
||||
|
||||
let parentProps = getContext("auth")
|
||||
|
||||
function removeMsg(msg) {
|
||||
if (msg.innerHTML!="") {
|
||||
msg.innerHTML = ""
|
||||
}
|
||||
}
|
||||
|
||||
function showDialog() {
|
||||
dialog.style.display = "block"
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (dialog!=null) {
|
||||
dialog.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function sendEmail() {
|
||||
let email = signUpField.value
|
||||
if (email.includes("@")) {
|
||||
sendText("/get-email",email)
|
||||
signUpField.value = ""
|
||||
signUpField.placeholder = "Subscribed!"
|
||||
signUpField.style.setProperty("--c", "hsl(147, 33%, 60%)")
|
||||
}
|
||||
else {
|
||||
signUpField.value = ""
|
||||
signUpField.placeholder = "must contain '@'"
|
||||
signUpField.style.setProperty("--c", "hsl(0, 100%, 60%)")
|
||||
}
|
||||
}
|
||||
|
||||
function clearField() {
|
||||
signUpField.placeholder = ""
|
||||
}
|
||||
|
||||
function renderGoogle() {
|
||||
if (parentProps.googleInit) {
|
||||
google.accounts.id.renderButton(googleButton,{
|
||||
theme: 'outline',
|
||||
size: 'large'
|
||||
})
|
||||
}
|
||||
else {
|
||||
setTimeout(renderGoogle,100)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
rememberMe.checked = true
|
||||
inputs = {email: emailInput, password: passwordInput}
|
||||
msgs = {email: emailMsg, password: passwordMsg}
|
||||
|
||||
document.addEventListener("keypress", function(event) {
|
||||
if (event.code == "Enter") {
|
||||
if (focused) {
|
||||
AuthTools.signup(msgs,inputs,toLandingPage)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//renderGoogle()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div id="signup-group" class="pane auth-pane" bind:this={signupGroup}>
|
||||
<h2 class="auth-title">SIGN UP</h2>
|
||||
<label class="auth-label" for="emailInput">Email </label><span bind:this={emailMsg} id="email-msg" on:change={() => removeMsg(emailMsg)}></span>
|
||||
<input bind:this={emailInput} id="emailInput" class="authEmailInput" type="email">
|
||||
<div class="password-field">
|
||||
<label class="auth-label" for="emailInput">Password </label><span bind:this={passwordMsg} id="password-msg"></span>
|
||||
<input bind:this={passwordInput} id="passwordInput" class="authPasswordInput" type="password" on:change={() => removeMsg(passwordMsg)}>
|
||||
<button bind:this={passwordVisibilityButton} class="eye-icon" on:click="{() => AuthTools.changePasswordVisibility(passwordVisibilityButton)}">
|
||||
<object type="image/svg+xml" data="/img/auth/eye_icon.svg" title="eye-icon"></object>
|
||||
</button>
|
||||
</div>
|
||||
<div id="remember-me">
|
||||
<input bind:this={rememberMe} type="checkbox" id="remember-me-checkbox"><label id="remember-me-label" for="passwordInput">remember me</label>
|
||||
</div>
|
||||
<button class="auth-button" on:click="{showDialog}">Sign up</button> <!--() => AuthTools.signup(msgs,inputs,AuthTools.toLandingPage)-->
|
||||
<p id="forgot-password"></p>
|
||||
<!--
|
||||
<hr class="auth-line">
|
||||
<div class="auth-methods-group">
|
||||
<button on:click="{showDialog}">
|
||||
<img src="/img/auth/google_icon.svg" id="navbar-logo" alt="google icon">
|
||||
</button>
|
||||
<button onclick="">
|
||||
<img src="img/auth/facebook_icon.svg" id="navbar-logo" alt="facebook icon">
|
||||
</button>
|
||||
<button onclick="">
|
||||
<img src="img/auth/linkedin_icon.svg" id="navbar-logo" alt="linkedin icon">
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div bind:this={dialog} id="dialog">
|
||||
<button id="shadow" on:click={hide}></button>
|
||||
<div id="wrapper" class="pane">
|
||||
<h2>Registration is closed</h2>
|
||||
<p>We are still in the process of opening.</p>
|
||||
<p>Sign up for updates to know when it becomes available:</p>
|
||||
<div id="newsletter-container">
|
||||
<input bind:this={signUpField} on:click={clearField} id="newsletterEmailInput" type="text">
|
||||
<button bind:this={signUp} on:click={sendEmail} id="newsletterEmailButton">sign up</button>
|
||||
</div>
|
||||
<button id="no-button" on:click={hide}>No thanks</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
@import '/css/auth.css';
|
||||
|
||||
#dialog {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#wrapper p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
#wrapper h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#shadow {
|
||||
position: fixed;
|
||||
cursor: default;
|
||||
top: 50%; right: 50%;
|
||||
transform: translate(50%,-50%);
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background:rgb(0, 0, 0, 0.2);
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
#newsletter-container {
|
||||
position: relative;
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#newsletterEmailInput {
|
||||
height: 2.5rem;
|
||||
border-radius: 0.2rem 0 0 0.2rem;
|
||||
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#newsletterEmailInput::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: var(--c,gray);
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
||||
#newsletterEmailButton {
|
||||
width: 6.8rem;
|
||||
height: 2.5rem;
|
||||
background: var(--pink);
|
||||
color: #ffffff;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.4rem;
|
||||
border-radius: 0 0.2rem 0.2rem 0;
|
||||
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
}
|
||||
|
||||
#newsletterEmailButton:active {
|
||||
background: var(--darker-pink);
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
top: 50%; right: 50%;
|
||||
transform: translate(50%,-50%);
|
||||
position: fixed;
|
||||
max-width: 36rem;
|
||||
width: 90vw;
|
||||
padding: 2rem 4rem;
|
||||
z-index: 1999999;
|
||||
}
|
||||
|
||||
#wrapper * {
|
||||
font-family: var(--sans-serif);
|
||||
}
|
||||
|
||||
#no-button {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 13rem;
|
||||
height: 3rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: #ffffff;
|
||||
border: 0.2rem solid var(--pink);
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.4rem;
|
||||
border-radius: 0.5rem;
|
||||
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
}
|
||||
|
||||
#no-button:active {
|
||||
background: hsl(343, 23%, 82%);
|
||||
}
|
||||
|
||||
</style>
|
415
Server/app/svelte/src/communes-add-component.svelte
Normal file
415
Server/app/svelte/src/communes-add-component.svelte
Normal file
@@ -0,0 +1,415 @@
|
||||
<svelte:options tag="communes-add-component" />
|
||||
|
||||
<script>
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { loadLocaleContent, getData, sendData } from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
import "/js/components/select-component.js"
|
||||
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/communes.json",callback)
|
||||
|
||||
|
||||
let confirmationMsg
|
||||
let addressInput
|
||||
let contactInput
|
||||
let statusInput
|
||||
let addressVec
|
||||
let userPinLat = 0
|
||||
let userPinLng = 0
|
||||
let userPin = createPin(0,0)
|
||||
userPin.setOpacity(0)
|
||||
|
||||
let locale = loadLocaleContent(content,"communes-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function createPin(lat,lng) {
|
||||
let markerIcon = new L.Icon({
|
||||
iconUrl: '/img/common/markers/marker-black.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
})
|
||||
return L.marker([lat,lng], {icon: markerIcon})
|
||||
}
|
||||
|
||||
function updatePin(marker,lat,lng) {
|
||||
let newLatLng = L.latLng(lat, lng); // Replace with the desired coordinates
|
||||
marker.setLatLng(newLatLng)
|
||||
}
|
||||
|
||||
function reverseGeocodeLocal(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressInput.value = fullAddress
|
||||
resizeInput(addressInput)
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function reverseGeocode(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2&accept-language=en`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressVec = [country,state,city]
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
|
||||
function addGroupPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field of ["location","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green")
|
||||
|
||||
userPin.addTo(map)
|
||||
map.on('click', function(event) {
|
||||
let lat = event.latlng.lat;
|
||||
let lng = event.latlng.lng;
|
||||
userPinLat = lat
|
||||
userPinLng = lng
|
||||
updatePin(userPin,lat,lng)
|
||||
userPin.setOpacity(1)
|
||||
reverseGeocodeLocal(lat, lng)
|
||||
reverseGeocode(lat, lng)
|
||||
})
|
||||
}
|
||||
|
||||
function updateConfirmationMsg(response) {
|
||||
if (response!==false) {
|
||||
|
||||
confirmationMsg.innerHTML = "You have been added to our database! Now go to our Discord to verify yourself."
|
||||
confirmationMsg.style.color = "green"
|
||||
}
|
||||
else {
|
||||
confirmationMsg.innerHTML = "Something went wrong."
|
||||
confirmationMsg.style.color = "red"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function submitLocation() {
|
||||
if (addressVec!=undefined) {
|
||||
let data = {
|
||||
country: addressVec[0],
|
||||
state: addressVec[1],
|
||||
town: addressVec[2],
|
||||
latitude: userPinLat,
|
||||
longitude: userPinLng,
|
||||
contact: contactInput.value,
|
||||
status: statusInput.value
|
||||
}
|
||||
|
||||
if (data.state=="") {
|
||||
data.state = null
|
||||
}
|
||||
if (data.town=="") {
|
||||
data.town = null
|
||||
}
|
||||
if (data.contact=="") {
|
||||
data.contact = null
|
||||
}
|
||||
let url = "/" + locale + "/communes-add-post/"
|
||||
sendData(url,data,updateConfirmationMsg)
|
||||
}
|
||||
}
|
||||
|
||||
function resizeInput(el) {
|
||||
el.nextElementSibling.innerHTML = el.value
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
<h1>Add a Commune</h1>
|
||||
<img id="communes-img" src="/img/common/communes.svg" alt="communes">
|
||||
<p class="description">If there are no communes near you who you can join and you want to start your own then do the following:</p>
|
||||
<ol>
|
||||
<li>Click on the map to show us where you are located;</li>
|
||||
<li>Add a way to contact you or leave blank for a pin to point to our discord;</li>
|
||||
<li>Press "Submit" to add yourself to our map;</li>
|
||||
<li>Verify yourself by having a chat with us at our Discord server to show on the map;</li>
|
||||
</ol>
|
||||
<div id="address-input-wrapper" class="input-label-wrapper">
|
||||
<label for="address-input">Location: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={addressInput} on:input={() => resizeInput(addressInput)} id="address-input" type="text" readonly>
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-label-wrapper">
|
||||
<label for="contact-input">Contact: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={contactInput} on:input={() => resizeInput(contactInput)} id="contact-input" type="text">
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="status-input-wrapper" class="input-label-wrapper">
|
||||
<label for="contact-input">Status: </label>
|
||||
<div class="input-wrapper">
|
||||
<select-component bind:this={statusInput} id="status-input" options={["forming","growing","not growing"]}></select-component>
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="submit-button" on:click={submitLocation}>Submit</button>
|
||||
<p id="confirmation-msg" bind:this={confirmationMsg}></p>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
#confirmation-msg {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin-left: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
font-family: var(--serif,serif);
|
||||
font-size: 1.15rem;
|
||||
line-height: 160%;
|
||||
color: #222222;
|
||||
width: 5.5rem;
|
||||
}
|
||||
|
||||
input, .ghost-input {
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--serif,serif);
|
||||
}
|
||||
|
||||
input {
|
||||
height: 2.5rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#address-input, #contact-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#address-input-wrapper {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#status-input {
|
||||
--width: 100%;
|
||||
--height: 2.5rem;
|
||||
--display: inline-block;
|
||||
--position: relative;
|
||||
--background-color: white;
|
||||
--border-radius: 0.4rem;
|
||||
--border: black solid 0.063rem;
|
||||
--padding-left: 0.5rem;
|
||||
--font-size: 1.15rem;
|
||||
--font-family: var(--serif, serif);
|
||||
}
|
||||
|
||||
#status-input-wrapper {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
|
||||
.ghost-input {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 5.5rem);
|
||||
min-width: min(20rem, calc(100% - 5.5rem));
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.input-label-wrapper {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.input-label-wrapper label {
|
||||
position: relative;
|
||||
top: 0.3rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#submit-button {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-top: 2rem;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.4rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
border: 0rem solid black;
|
||||
border-radius: 0.5rem;
|
||||
background: #cb1816;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#communes-img {
|
||||
position: absolute;
|
||||
width: 11.5rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
z-index: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
#text-container>:nth-child(3) {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-top: 2rem;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
position: relative;
|
||||
max-width: calc(100vw - 4rem);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 2.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
#container p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
</style>
|
@@ -3,9 +3,10 @@
|
||||
<script>
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { communesByCountry, addMarkersCommunes } from '/js/communes.js'
|
||||
import { loadLocaleContent } from "/js/libraries/serverTools.js"
|
||||
import { writable } from 'svelte/store'
|
||||
import { loadLocaleContent, getData} from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addCommunePinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -13,13 +14,48 @@
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
let locale = loadLocaleContent(content,"communes-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function mapCallbackCommunes(createMap,content) {
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/communes.json",callback)
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersCommunes(map,content)
|
||||
let options = {
|
||||
enableCountryGrouping: true,
|
||||
}
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addCommunePinContent,"red",options)
|
||||
}
|
||||
|
||||
function getCountry(x) {
|
||||
return locale=="en" ? x : translate($content,x)
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -28,25 +64,27 @@
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==2}
|
||||
{#if $loaded==3}
|
||||
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
<h1>{$content.communes}</h1>
|
||||
<img id="commune-img" src="/img/common/commune.svg" alt="commune">
|
||||
<img id="communes-img" src="/img/common/communes.svg" alt="commune">
|
||||
<p class="description">{$content.p1}</p>
|
||||
<h3>{$content.subheading1}</h3>
|
||||
<map-component id="map" callback={(createMap) => mapCallbackCommunes(createMap,$content,locale)}></map-component>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
<p id="add-prompt">{$content["map-prompt"]}</p>
|
||||
{#each Object.entries(communesByCountry) as [name,communes]}
|
||||
<h4 class="country-name">{$content[name]}</h4>
|
||||
{#each Object.entries(entriesByCountry) as [name,entries]}
|
||||
<h4 class="country-name">{getCountry(name)}</h4>
|
||||
<div class="country-block">
|
||||
{#each communes as commune}
|
||||
{#each entries as entry}
|
||||
|
||||
<div class="location-info">
|
||||
<p><b>{$content.location}: </b>{commune.location[0].map(x => $content[x]).join(", ")}</p>
|
||||
<p><b>{$content.status}: </b>{$content[commune.status]}</p>
|
||||
<p><b>{$content.members}: </b>{commune.members}</p>
|
||||
<p><b>{$content.contact}: </b><a href={commune.contact[0]} target=;_blank; rel=noreferrer>{$content[commune.contact[1]]}</a></p>
|
||||
<p><b>{$content.location}: </b>{getAddress(entry)}</p>
|
||||
<p><b>{$content.status}: </b>{$content[entry.status]}</p>
|
||||
<p><b>{$content.members}: </b>{entry.members}</p>
|
||||
<p><b>{$content.contact}: </b><a href={entry.contact} target=;_blank; rel=noreferrer>{entry.contact}</a></p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -67,7 +105,7 @@
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#commune-img {
|
||||
#communes-img {
|
||||
position: absolute;
|
||||
width: 11.5rem;
|
||||
left: 50%;
|
||||
|
@@ -9,6 +9,7 @@
|
||||
// Export statements
|
||||
export let callback = null
|
||||
export let colors = null
|
||||
export let map = null
|
||||
|
||||
// Main code
|
||||
let mapContainer
|
||||
@@ -50,6 +51,8 @@
|
||||
width: var(--width,100%);
|
||||
margin-top: var(--margin-top,0);
|
||||
margin-bottom: var(--margin-bottom,0);
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
</style>
|
@@ -71,17 +71,17 @@
|
||||
<div bind:this={root} id="root" class="pane-centering">
|
||||
<div class="pane-container">
|
||||
<div id="sidebars-left" class="sidebar">
|
||||
<div bind:this={sidebarLeft} id="sidebar-left" class="pane">
|
||||
<div bind:this={sidebarLeft} id="sidebar-left">
|
||||
<slot name="sidebar-left"></slot>
|
||||
</div>
|
||||
<div bind:this={sidebarLeft2} id="sidebar-left2" class="pane">
|
||||
<div bind:this={sidebarLeft2} id="sidebar-left2">
|
||||
<slot name="sidebar-left2"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div bind:this={sidebarRight} id="sidebar-right" class="pane sidebar">
|
||||
<slot name="sidebar-right"></slot>
|
||||
</div>
|
||||
<div bind:this={mainPane} id="main-pane" class="pane">
|
||||
<div bind:this={mainPane} id="main-pane">
|
||||
<slot name="main" id="main-slot"></slot>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,7 +95,10 @@
|
||||
}
|
||||
|
||||
#root {
|
||||
position: relative;
|
||||
margin-top: auto;
|
||||
min-height: var(--min-height,auto);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main-pane {
|
||||
@@ -105,10 +108,8 @@
|
||||
padding-top: var(--padding-top,0rem);
|
||||
padding-bottom: var(--padding-bottom,0rem);
|
||||
text-align: justify;
|
||||
background: var(--background,white);
|
||||
box-shadow: var(--box-shadow,0 0 0.314rem rgb(187, 187, 187));
|
||||
margin: auto;
|
||||
height: min-content;
|
||||
height: 100%;
|
||||
max-width: var(--width-main,66rem);
|
||||
width: var(--width-main,66rem);
|
||||
z-index: 1;
|
||||
@@ -124,13 +125,14 @@
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-left: calc(-1*var(--width-left,22.5rem) - 1rem - 4rem);
|
||||
width: calc(var(--width-left,22.5rem) + 4rem);
|
||||
width: max-content;
|
||||
max-width: 30rem;
|
||||
}
|
||||
|
||||
#sidebar-left,#sidebar-left2 {
|
||||
position: relative;
|
||||
background-color: white;
|
||||
padding: 2rem 2rem;
|
||||
padding: 0rem 0rem;
|
||||
}
|
||||
|
||||
#sidebar-left {
|
||||
@@ -149,16 +151,16 @@
|
||||
padding: 2rem 2rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1880px) {
|
||||
@media only screen and (max-width: 1340px) {
|
||||
|
||||
#main-pane {
|
||||
max-width: initial;
|
||||
width: 100%;
|
||||
max-width: var(--width-main,66rem);
|
||||
padding-left: var(--padding-left-mobile,1.8rem);
|
||||
padding-right: var(--padding-right-mobile,1.8rem);
|
||||
padding-top: var(--padding-top-mobile,1.8rem);
|
||||
padding-bottom: var(--padding-bottom-mobile,1.8rem);
|
||||
padding-left: var(--padding-left-mobile,0rem);
|
||||
padding-right: var(--padding-right-mobile,0rem);
|
||||
padding-top: var(--padding-top-mobile,0rem);
|
||||
padding-bottom: var(--padding-bottom-mobile,0rem);
|
||||
}
|
||||
|
||||
#sidebars-left, #sidebar-right {
|
||||
|
@@ -194,6 +194,7 @@
|
||||
.select span {
|
||||
position: relative;
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 0.3rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
413
Server/app/svelte/src/cooperatives-add-component.svelte
Normal file
413
Server/app/svelte/src/cooperatives-add-component.svelte
Normal file
@@ -0,0 +1,413 @@
|
||||
<svelte:options tag="cooperatives-add-component" />
|
||||
|
||||
<script>
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { loadLocaleContent, getData, sendData } from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/groups.json",callback)
|
||||
|
||||
|
||||
let confirmationMsg
|
||||
let addressInput
|
||||
let contactInput
|
||||
let addressVec
|
||||
let userPinLat = 0
|
||||
let userPinLng = 0
|
||||
let userPin = createPin(0,0)
|
||||
userPin.setOpacity(0)
|
||||
|
||||
let locale = loadLocaleContent(content,"groups-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function createPin(lat,lng) {
|
||||
let markerIcon = new L.Icon({
|
||||
iconUrl: '/img/common/markers/marker-black.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
})
|
||||
return L.marker([lat,lng], {icon: markerIcon})
|
||||
}
|
||||
|
||||
function updatePin(marker,lat,lng) {
|
||||
let newLatLng = L.latLng(lat, lng); // Replace with the desired coordinates
|
||||
marker.setLatLng(newLatLng)
|
||||
}
|
||||
|
||||
function reverseGeocodeLocal(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressInput.value = fullAddress
|
||||
resizeInput(addressInput)
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function reverseGeocode(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2&accept-language=en`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressVec = [country,state,city]
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function addGroupPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field of ["location","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green")
|
||||
|
||||
userPin.addTo(map)
|
||||
map.on('click', function(event) {
|
||||
let lat = event.latlng.lat;
|
||||
let lng = event.latlng.lng;
|
||||
userPinLat = lat
|
||||
userPinLng = lng
|
||||
updatePin(userPin,lat,lng)
|
||||
userPin.setOpacity(1)
|
||||
reverseGeocodeLocal(lat, lng)
|
||||
reverseGeocode(lat, lng)
|
||||
})
|
||||
}
|
||||
|
||||
function updateConfirmationMsg(response) {
|
||||
if (response!==false) {
|
||||
|
||||
confirmationMsg.innerHTML = "You have been added to our database! Now go to our Discord to verify yourself."
|
||||
confirmationMsg.style.color = "green"
|
||||
}
|
||||
else {
|
||||
confirmationMsg.innerHTML = "Something went wrong."
|
||||
confirmationMsg.style.color = "red"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function submitLocation() {
|
||||
if (addressVec!=undefined) {
|
||||
let data = {
|
||||
country: addressVec[0],
|
||||
state: addressVec[1],
|
||||
town: addressVec[2],
|
||||
latitude: userPinLat,
|
||||
longitude: userPinLng,
|
||||
contact: contactInput.value
|
||||
}
|
||||
|
||||
if (data.state=="") {
|
||||
data.state = null
|
||||
}
|
||||
if (data.town=="") {
|
||||
data.town = null
|
||||
}
|
||||
if (data.contact=="") {
|
||||
data.contact = null
|
||||
}
|
||||
let url = "/" + locale + "/groups-add-post/"
|
||||
sendData(url,data,updateConfirmationMsg)
|
||||
}
|
||||
}
|
||||
|
||||
function resizeInput(el) {
|
||||
el.nextElementSibling.innerHTML = el.value
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
<h1>Add a Group</h1>
|
||||
<img id="groups-img" src="/img/common/groups.svg" alt="groups">
|
||||
<p class="description">If there are no groups in your town with whom you can organize then do the following:</p>
|
||||
<ol>
|
||||
<li>Click on the map to show us where you are located;</li>
|
||||
<li>Add a way to contact you or leave blank for a pin to point to our discord;</li>
|
||||
<li>Press "Submit" to add yourself to our map;</li>
|
||||
<li>Verify yourself by having a chat with us at our Discord server to show on the map;</li>
|
||||
</ol>
|
||||
<div id="address-input-wrapper" class="input-label-wrapper">
|
||||
<label for="address-input">Location: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={addressInput} on:input={() => resizeInput(addressInput)} id="address-input" type="text" readonly>
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-label-wrapper">
|
||||
<label for="contact-input">Contact: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={contactInput} on:input={() => resizeInput(contactInput)} id="contact-input" type="text">
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="submit-button" on:click={submitLocation}>Submit</button>
|
||||
<p id="confirmation-msg" bind:this={confirmationMsg}></p>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
#confirmation-msg {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin-left: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
font-family: var(--serif,serif);
|
||||
font-size: 1.15rem;
|
||||
line-height: 160%;
|
||||
color: #222222;
|
||||
width: 5.5rem;
|
||||
}
|
||||
|
||||
input, .ghost-input {
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--serif,serif);
|
||||
}
|
||||
|
||||
input {
|
||||
height: 2.5rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#address-input, #contact-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#address-input-wrapper {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ghost-input {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 5.5rem);
|
||||
min-width: min(20rem, calc(100% - 5.5rem));
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.input-label-wrapper {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.input-label-wrapper label {
|
||||
position: relative;
|
||||
top: 0.3rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#submit-button {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-top: 2rem;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.4rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
border: 0rem solid black;
|
||||
border-radius: 0.5rem;
|
||||
background: #cb1816;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#add-prompt {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#groups-img {
|
||||
position: absolute;
|
||||
width: 14rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
z-index: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
#text-container>:nth-child(3) {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
.country-name {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.country-block {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.location-info {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.location-info p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #DD1C1A;
|
||||
}
|
||||
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-top: 2rem;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
position: relative;
|
||||
max-width: calc(100vw - 4rem);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 2.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
#container p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
</style>
|
@@ -4,8 +4,9 @@
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { coopsByCountry, addMarkersCoops } from '/js/coops.js'
|
||||
import { loadLocaleContent } from "/js/libraries/serverTools.js"
|
||||
import { loadLocaleContent, getData} from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addCoopPinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -13,13 +14,48 @@
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
let locale = loadLocaleContent(content,"cooperatives-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function mapCallbackCoops(createMap,content) {
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/cooperatives.json",callback)
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersCoops(map,content)
|
||||
let options = {
|
||||
enableCountryGrouping: true,
|
||||
}
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addCoopPinContent,"blue",options)
|
||||
}
|
||||
|
||||
function getCountry(x) {
|
||||
return locale=="en" ? x : translate($content,x)
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -28,37 +64,38 @@
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==2}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<div id="text-container">
|
||||
<h1>{$content.cooperatives}</h1>
|
||||
<img id="coops-img" src="/img/common/coops.svg" alt="coops">
|
||||
<p class="description">{$content.p1}</p>
|
||||
<h3>{$content.subheading1}</h3>
|
||||
<map-component id="map" callback={(createMap) => mapCallbackCoops(createMap,$content,locale)}></map-component>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
<p id="add-prompt">{$content["map-prompt"]}</p>
|
||||
{#each Object.entries(coopsByCountry) as [name,coops]}
|
||||
<h4 class="country-name">{$content[name]}</h4>
|
||||
{#each Object.entries(entriesByCountry) as [name,entries]}
|
||||
<h4 class="country-name">{getCountry(name)}</h4>
|
||||
<div class="country-block">
|
||||
{#each coops as coop}
|
||||
{#each entries as entry}
|
||||
{console.log(entry)}
|
||||
<div class="location-info">
|
||||
<div class="img-general-info">
|
||||
<div>
|
||||
<p><b>{$content.name}: </b>{coop.name}</p>
|
||||
<p><b>{$content.location}: </b>{coop.location[0].map(x => $content[x]).join(", ")}</p>
|
||||
<p><b>{$content.market}: </b>{$content[coop.market]}</p>
|
||||
<p><b>{$content.workers}: </b>{coop.workers}</p>
|
||||
<p><b>{$content.status}: </b>{$content[coop.status]}</p>
|
||||
<p><b>{$content.website}: </b><a href={"https://www."+coop.website} target="_blank" rel=noreferrer>{coop.website}</a></p>
|
||||
<p><b>{$content.contact}: </b><a href={coop.contact[0]} target=;_blank; rel=noreferrer>{$content[coop.contact[1]]}</a></p>
|
||||
<p><b>{$content.name}: </b>{entry.name}</p>
|
||||
<p><b>{$content.location}: </b>{getAddress(entry)}</p>
|
||||
<p><b>{$content.market}: </b>{entry.market}</p>
|
||||
<p><b>{$content.workers}: </b>{entry.workers}</p>
|
||||
<p><b>{$content.status}: </b>{entry.status}</p>
|
||||
<p><b>{$content.website}: </b><a href={entry.website} target="_blank" rel=noreferrer>{entry.website}</a></p>
|
||||
<p><b>{$content.contact}: </b><a href={entry.contact} target=;_blank; rel=noreferrer>{entry.contact}</a></p>
|
||||
</div>
|
||||
<picture>
|
||||
<source srcset={"/img/coops/"+coop.logo+".webp"}>
|
||||
<source srcset={"/img/coops/"+coop.logo+".png"}>
|
||||
<source srcset={"/img/coops/"+entry.logo+".webp"}>
|
||||
<source srcset={"/img/coops/"+entry.logo+".png"}>
|
||||
<img class="coop-logo" alt="logo">
|
||||
</picture>
|
||||
</div>
|
||||
<p><b>{$content.description}: </b>{$content[coop.description]}</p>
|
||||
<p><b>{$content.description}: </b>{entry.description}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@@ -32,8 +32,8 @@
|
||||
<button on:click={() => {location.href='#'}} id="footer-up" aria-label="go up">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="42.545" height="72.601" viewBox="0 0 42.545 72.601">
|
||||
<g id="Group_268" data-name="Group 268" transform="translate(-6.177 -2.399)">
|
||||
<rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="#cb1816"/>
|
||||
<path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="#DD1C1A"/>
|
||||
<rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="var(--red)"/>
|
||||
<path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="var(--red)"/>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -54,8 +54,8 @@ footer {
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: #5B6970;/*var(--dark-green);*/
|
||||
border-top: #cb1816 solid 0.5rem;
|
||||
background: var(--gray);
|
||||
border-top: var(--red) solid 0.5rem;
|
||||
}
|
||||
|
||||
footer p, footer a {
|
||||
|
576
Server/app/svelte/src/groups-add-component.svelte
Normal file
576
Server/app/svelte/src/groups-add-component.svelte
Normal file
@@ -0,0 +1,576 @@
|
||||
<svelte:options tag="groups-add-component" />
|
||||
|
||||
<script>
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { loadLocaleContent, getData, sendData } from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
|
||||
// Export statements
|
||||
export let map = null
|
||||
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
let userData
|
||||
|
||||
let buttonsGroupMember
|
||||
let buttonsNotGroupMember
|
||||
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/groups.json",callback)
|
||||
|
||||
|
||||
let confirmationMsg
|
||||
let addressInput
|
||||
let contactInput
|
||||
let membersInput
|
||||
let addressVec = ["","",""]
|
||||
let userPinData = {
|
||||
|
||||
}
|
||||
let userPinLng = 0
|
||||
let userPin = createPin(0,0)
|
||||
userPin.setOpacity(0)
|
||||
let modeButtons = []
|
||||
|
||||
let context = getContext("profile-component")
|
||||
let closeGroupsAdd = context.closeGroupsAdd
|
||||
let maps = context.maps
|
||||
let onLoadedGroups = context.onLoadedGroups
|
||||
let userGroups = context.userGroups
|
||||
let user = context.user
|
||||
|
||||
let has_group = userGroups.length!=0
|
||||
let mode = has_group ? 2 : 0
|
||||
let pendingGroup
|
||||
if (has_group) {
|
||||
pendingGroup= userGroups[0].status!=undefined
|
||||
if (pendingGroup) {
|
||||
mode = 3
|
||||
}
|
||||
}
|
||||
|
||||
let locale = loadLocaleContent(content,"groups-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function createPin(lat,lng) {
|
||||
let markerIcon = new L.Icon({
|
||||
iconUrl: '/img/common/markers/marker-black.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
})
|
||||
return L.marker([lat,lng], {icon: markerIcon})
|
||||
}
|
||||
|
||||
function updatePin(marker,lat,lng) {
|
||||
let newLatLng = L.latLng(lat, lng); // Replace with the desired coordinates
|
||||
marker.setLatLng(newLatLng)
|
||||
}
|
||||
|
||||
function reverseGeocodeLocal(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressInput.value = fullAddress
|
||||
resizeInput(addressInput)
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function reverseGeocode(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2&accept-language=en`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
if (address!=undefined) {
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressVec = [country,state,city]
|
||||
}
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function addGroupPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field of ["location","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
map = createMap([22, 0],2)
|
||||
maps["groupsAdd"] = map
|
||||
let options = {
|
||||
enableCountryGrouping: false,
|
||||
pinCallback: pinCallback
|
||||
}
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green",options)
|
||||
|
||||
userPin.addTo(map)
|
||||
map.on('click', function(event) {
|
||||
if (mode==0) {
|
||||
let lat = event.latlng.lat;
|
||||
let lng = event.latlng.lng;
|
||||
userPinData["latitude"] = lat
|
||||
userPinData["longitude"] = lng
|
||||
userPinData["id"] = null
|
||||
updatePin(userPin,lat,lng)
|
||||
userPin.setOpacity(1)
|
||||
reverseGeocodeLocal(lat, lng)
|
||||
reverseGeocode(lat, lng)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateConfirmationMsg(response) {
|
||||
if (response!==false) {
|
||||
if (mode==0 && !user.verified) {
|
||||
confirmationMsg.innerHTML = "You have been added to our database! Now go to our Discord to verify yourself."
|
||||
}
|
||||
else {
|
||||
confirmationMsg.innerHTML = "Success!"
|
||||
}
|
||||
confirmationMsg.style.color = "green"
|
||||
if (mode==0 || mode==1) {
|
||||
userGroups[0] = {}
|
||||
}
|
||||
userGroups[0].country = addressVec[0]=="" ? null : addressVec[0]
|
||||
userGroups[0].state = addressVec[1]=="" ? null : addressVec[1]
|
||||
userGroups[0].town = addressVec[2]=="" ? null : addressVec[2]
|
||||
userGroups[0].members = userPinData["members"]
|
||||
onLoadedGroups()
|
||||
}
|
||||
else {
|
||||
confirmationMsg.innerHTML = "Something went wrong."
|
||||
confirmationMsg.style.color = "red"
|
||||
}
|
||||
}
|
||||
|
||||
function submitLocation() {
|
||||
if (addressVec[0]!="" || mode==3) {
|
||||
let membersVal, contactVal
|
||||
if (mode==0) { // Create
|
||||
membersVal = membersInput.value
|
||||
contactVal = contactInput.value
|
||||
}
|
||||
else if (mode==1) { // Join
|
||||
contactVal = contactInput.value
|
||||
}
|
||||
else if (mode==2 || mode==3) { // Move
|
||||
membersVal = ""
|
||||
contactVal = ""
|
||||
}
|
||||
else if (mode==3) { // Leave
|
||||
membersVal = ""
|
||||
contactVal = ""
|
||||
addressVec = [null,null,null]
|
||||
userPinData["latitude"] = null
|
||||
userPinData["longitude"] = null
|
||||
}
|
||||
|
||||
userData = {
|
||||
country: addressVec[0],
|
||||
state: addressVec[1],
|
||||
town: addressVec[2],
|
||||
latitude: userPinData["latitude"],
|
||||
longitude: userPinData["longitude"],
|
||||
contact: contactVal=="" ? null : contactVal,
|
||||
members: membersVal=="" ? null : parseInt(membersVal),
|
||||
group_id: userPinData["id"],
|
||||
mode: mode
|
||||
}
|
||||
|
||||
if (userData.state=="") {
|
||||
userData.state = null
|
||||
}
|
||||
if (userData.town=="") {
|
||||
userData.town = null
|
||||
}
|
||||
let url = "/" + locale + "/groups-add-post/"
|
||||
sendData(url,userData,updateConfirmationMsg)
|
||||
}
|
||||
}
|
||||
|
||||
function resizeInput(el) {
|
||||
el.nextElementSibling.innerHTML = el.value
|
||||
}
|
||||
|
||||
function pinCallback(marker,event) {
|
||||
if (mode==1) {
|
||||
let lat = event.latlng.lat;
|
||||
let lng = event.latlng.lng;
|
||||
userPinData["latitude"] = lat
|
||||
userPinData["longitude"] = lng
|
||||
userPinData["id"] = marker.id
|
||||
userPinData["members"] = marker.members
|
||||
updatePin(userPin,lat,lng)
|
||||
userPin.setOpacity(1)
|
||||
reverseGeocodeLocal(lat, lng)
|
||||
reverseGeocode(lat, lng)
|
||||
}
|
||||
}
|
||||
|
||||
function chooseButton(index) {
|
||||
for (let b of modeButtons) {
|
||||
if (b!=undefined) {
|
||||
b.style.background = "rgba(197, 43, 40, 0.319)"
|
||||
b.style.color = "black"
|
||||
}
|
||||
}
|
||||
modeButtons[index].style.background = "rgb(197, 43, 40)"
|
||||
modeButtons[index].style.color = "white"
|
||||
mode = index
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
if (g!=undefined) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
else {
|
||||
return "Create or join group"
|
||||
}
|
||||
}
|
||||
|
||||
function onLoaded() {
|
||||
if ($loaded==3) {
|
||||
chooseButton(mode)
|
||||
if (mode==2 || mode==3) {
|
||||
addressInput.value = getAddress(userGroups[0])
|
||||
}
|
||||
}
|
||||
else {
|
||||
let f = () => onLoaded()
|
||||
setTimeout(f, 100)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
onLoaded()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<button class="close-button" on:click={closeGroupsAdd}></button>
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
{#if !has_group}
|
||||
<div bind:this={buttonsNotGroupMember} id="button-line">
|
||||
<button bind:this={modeButtons[0]} on:click={() => chooseButton(0)}>Create</button>
|
||||
<button bind:this={modeButtons[1]} on:click={() => chooseButton(1)}>Join</button>
|
||||
</div>
|
||||
{:else if has_group && !pendingGroup}
|
||||
<div bind:this={buttonsGroupMember} id="button-line">
|
||||
<button bind:this={modeButtons[2]} on:click={() => chooseButton(2)} style={"display: " + (pendingGroup ? "none" : "initial")}>Move</button>
|
||||
<button bind:this={modeButtons[3]} on:click={() => chooseButton(3)}>Leave</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div bind:this={buttonsGroupMember} id="button-line">
|
||||
<button bind:this={modeButtons[3]} on:click={() => chooseButton(3)}>Leave</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div id="address-input-wrapper" class="input-label-wrapper">
|
||||
<label for="address-input">Location: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={addressInput} on:input={() => resizeInput(addressInput)} id="address-input" type="text" readonly>
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
{#key mode}
|
||||
{#if mode==0}
|
||||
<div id="members-input-wrapper" class="input-label-wrapper">
|
||||
<label for="members-input">Members: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={membersInput} id="members-input" type="text" value={1}>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if mode==0 || mode==1}
|
||||
<div class="input-label-wrapper">
|
||||
<label for="contact-input">Contact: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={contactInput} on:input={() => resizeInput(contactInput)} id="contact-input" type="text">
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
<button id="submit-button" on:click={submitLocation}>Submit</button>
|
||||
<p id="confirmation-msg" bind:this={confirmationMsg}></p>
|
||||
{#if !(has_group && pendingGroup)}
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
#button-line {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
#button-line button{
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.15rem;
|
||||
padding: 1rem 0;
|
||||
width: 7rem;
|
||||
}
|
||||
|
||||
#button-line :first-child {
|
||||
border-top-left-radius: 1rem;
|
||||
border-bottom-left-radius: 1rem;
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
|
||||
#button-line :last-child {
|
||||
margin-left: 0.1rem;
|
||||
border-top-right-radius: 1rem;
|
||||
border-bottom-right-radius: 1rem;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
right: 0rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.close-button::before,
|
||||
.close-button::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0.2rem;
|
||||
height: 2rem;
|
||||
background-color: #000;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.close-button::before {
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
}
|
||||
|
||||
.close-button::after {
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
|
||||
#confirmation-msg {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin-left: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
font-family: var(--serif,serif);
|
||||
font-size: 1.15rem;
|
||||
line-height: 160%;
|
||||
color: #222222;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
input, .ghost-input {
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--serif,serif);
|
||||
}
|
||||
|
||||
input {
|
||||
height: 2.5rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#address-input, #contact-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#address-input-wrapper {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#members-input-wrapper {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#members-input {
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
.ghost-input {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 5.5rem);
|
||||
min-width: min(20rem, calc(100% - 5.5rem));
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.input-label-wrapper {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.input-label-wrapper label {
|
||||
position: relative;
|
||||
top: 0.3rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#submit-button {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-top: 2rem;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.4rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
border: 0rem solid black;
|
||||
border-radius: 0.5rem;
|
||||
background: #cb1816;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-top: 2rem;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
position: relative;
|
||||
max-width: calc(100vw - 4rem);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
#container p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
</style>
|
@@ -4,8 +4,9 @@
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { groupsByCountry, addMarkersGroups } from '/js/groups.js'
|
||||
import { loadLocaleContent} from "/js/libraries/serverTools.js"
|
||||
import { loadLocaleContent, getData} from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addGroupPinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -13,22 +14,54 @@
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
loadLocaleContent(content,"groups-component",loaded)
|
||||
let locale = loadLocaleContent(content,"groups-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function mapCallbackGroups(createMap,content) {
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/groups.json",callback)
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersGroups(map,content)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green")
|
||||
}
|
||||
|
||||
function getCountry(x) {
|
||||
return locale=="en" ? x : translate($content,x)
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// {console.log(loaded)}
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==2}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
@@ -36,16 +69,16 @@
|
||||
<img id="groups-img" src="/img/common/groups.svg" alt="groups">
|
||||
<p class="description">{$content.p1}</p>
|
||||
<h3>{$content.subheading1}</h3>
|
||||
<map-component id="map" callback={(createMap) => mapCallbackGroups(createMap,$content)}></map-component>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
<p id="add-prompt">{$content["map-prompt"]}</p>
|
||||
{#each Object.entries(groupsByCountry) as [name,groups]}
|
||||
<h4 class="country-name">{$content[name]}</h4>
|
||||
{#each Object.entries(entriesByCountry) as [name,entries]}
|
||||
<h4 class="country-name">{getCountry(name)}</h4>
|
||||
<div class="country-block">
|
||||
{#each groups as group}
|
||||
{#each entries as entry}
|
||||
<div class="location-info">
|
||||
<p><b>{$content.location}: </b>{group.location[0].map(x => $content[x]).join(", ")}</p>
|
||||
<p><b>{$content.members}: </b>{group.members}</p>
|
||||
<p><b>{$content.contact}: </b><a href={group.contact[0]} target=;_blank; rel=noreferrer>{$content[group.contact[1]]}</a></p>
|
||||
<p><b>{$content.location}: </b>{getAddress(entry)}</p>
|
||||
<p><b>{$content.members}: </b>{entry.members}</p>
|
||||
<p><b>{$content.contact}: </b><a href={entry.contact} target=;_blank; rel=noreferrer>{entry.contact}</a></p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -102,7 +135,7 @@
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-bottom: 3rem;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
|
@@ -4,11 +4,9 @@
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { addMarkersGroups, groupsMarkersLayer } from '/js/groups.js'
|
||||
import { addMarkersCoops, coopsMarkersLayer } from '/js/coops.js'
|
||||
import { addMarkersCommunes, communesMarkersLayer } from '/js/communes.js'
|
||||
import { addMarkersParties, partiesMarkersLayer } from '/js/parties.js'
|
||||
import { loadLocaleContent } from "/js/libraries/serverTools.js"
|
||||
import { loadLocaleContent, getData } from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addGroupPinContent, addCommunePinContent, addCoopPinContent, addPartyPinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -16,6 +14,8 @@
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries = {}
|
||||
let entriesByCountry ={}
|
||||
|
||||
loadLocaleContent(content,"groups-component",loaded)
|
||||
loadLocaleContent(content,"communes-component",loaded)
|
||||
@@ -24,19 +24,43 @@
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
let locale = loadLocaleContent(content,"join-us-component",loaded)
|
||||
|
||||
function mapCallback(createMap,content) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersGroups(map,content)
|
||||
addMarkersCommunes(map,content)
|
||||
addMarkersCoops(map,content)
|
||||
addMarkersParties(map,content)
|
||||
|
||||
let overlayMaps = {
|
||||
"Groups": groupsMarkersLayer,
|
||||
"Communes": communesMarkersLayer,
|
||||
"Coops": coopsMarkersLayer,
|
||||
"Parties": partiesMarkersLayer,
|
||||
let callback = (response,name) => {
|
||||
entries[name] = JSON.parse(response)
|
||||
entriesByCountry[name] = {}
|
||||
for (let g of entries[name]) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry[name]) {
|
||||
entriesByCountry[name][country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[name][country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/groups.json",(response) => callback(response,"groups"))
|
||||
getData("/assets/communes.json",(response) => callback(response,"communes"))
|
||||
getData("/assets/cooperatives.json",(response) => callback(response,"cooperatives"))
|
||||
getData("/assets/parties.json",(response) => callback(response,"parties"))
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
let groupsMarkersLayer = addMarkersEntries(entries["groups"],entriesByCountry["groups"],map,content,locale,addGroupPinContent,"green")
|
||||
let communesMarkersLayer = addMarkersEntries(entries["communes"],entriesByCountry["communes"],map,content,locale,addCommunePinContent,"red")
|
||||
let coopsMarkersLayer = addMarkersEntries(entries["cooperatives"],entriesByCountry["cooperatives"],map,content,locale,addCoopPinContent,"blue")
|
||||
let partiesMarkersLayer = addMarkersEntries(entries["parties"],entriesByCountry["parties"],map,content,locale,addPartyPinContent,"gold")
|
||||
|
||||
let overlayMaps = {}
|
||||
overlayMaps[content.groups] = groupsMarkersLayer
|
||||
overlayMaps[content.communes] = communesMarkersLayer
|
||||
overlayMaps[content.cooperatives] = coopsMarkersLayer
|
||||
overlayMaps[content.parties] = partiesMarkersLayer
|
||||
L.control.layers(null, overlayMaps).addTo(map)
|
||||
}
|
||||
|
||||
@@ -46,7 +70,7 @@
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==6}
|
||||
{#if $loaded==10}
|
||||
<div id="container">
|
||||
<div id="text-container">
|
||||
<h1>{$content.heading}</h1>
|
||||
@@ -77,7 +101,7 @@
|
||||
<p>{$content.nearYou}</p>
|
||||
</div>
|
||||
<p>{$content.noneNear} <a href="https://chat.whatsapp.com/BhnmUNljUxJ2AjeHUwyTKh" target="_blank" rel=noreferrer>{$content.WhatsAppGroup}</a> {$content.or} <a href="https://discord.gg/Qk8KUk787z" target="_blank" rel=noreferrer>{$content.DiscordServer}</a>{$content.helpStart}</p>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content)}></map-component>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)} colors={["#23AC20","#CA2437","#217BC9","#FFD326"]}></map-component>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
@@ -4,11 +4,9 @@
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { addMarkersGroups, groupsMarkersLayer } from '/js/groups.js'
|
||||
import { addMarkersCoops, coopsMarkersLayer } from '/js/coops.js'
|
||||
import { addMarkersCommunes, communesMarkersLayer } from '/js/communes.js'
|
||||
import { addMarkersParties, partiesMarkersLayer } from '/js/parties.js'
|
||||
import { loadLocaleContent } from "/js/libraries/serverTools.js"
|
||||
import { loadLocaleContent, getData } from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addGroupPinContent, addCommunePinContent, addCoopPinContent, addPartyPinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -18,6 +16,8 @@
|
||||
let gridWidth
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries = {}
|
||||
let entriesByCountry ={}
|
||||
|
||||
function changeWidth(locale) {
|
||||
if (locale=="ru") {
|
||||
@@ -28,6 +28,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
let callback = (response,name) => {
|
||||
entries[name] = JSON.parse(response)
|
||||
entriesByCountry[name] = {}
|
||||
for (let g of entries[name]) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry[name]) {
|
||||
entriesByCountry[name][country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[name][country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/groups.json",(response) => callback(response,"groups"))
|
||||
getData("/assets/communes.json",(response) => callback(response,"communes"))
|
||||
getData("/assets/cooperatives.json",(response) => callback(response,"cooperatives"))
|
||||
getData("/assets/parties.json",(response) => callback(response,"parties"))
|
||||
|
||||
loadLocaleContent(content,"groups-component",loaded)
|
||||
loadLocaleContent(content,"communes-component",loaded)
|
||||
loadLocaleContent(content,"cooperatives-component",loaded)
|
||||
@@ -36,12 +60,15 @@
|
||||
let locale = loadLocaleContent(content,"landing-component",loaded,changeWidth)
|
||||
changeWidth(locale)
|
||||
|
||||
function mapCallback(createMap,content) {
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersGroups(map,content)
|
||||
addMarkersCommunes(map,content)
|
||||
addMarkersCoops(map,content)
|
||||
addMarkersParties(map,content)
|
||||
let options = {
|
||||
enableCountryGrouping: true,
|
||||
}
|
||||
let groupsMarkersLayer = addMarkersEntries(entries["groups"],entriesByCountry["groups"],map,content,locale,addGroupPinContent,"green",options)
|
||||
let communesMarkersLayer = addMarkersEntries(entries["communes"],entriesByCountry["communes"],map,content,locale,addCommunePinContent,"red",options)
|
||||
let coopsMarkersLayer = addMarkersEntries(entries["cooperatives"],entriesByCountry["cooperatives"],map,content,locale,addCoopPinContent,"blue",options)
|
||||
let partiesMarkersLayer = addMarkersEntries(entries["parties"],entriesByCountry["parties"],map,content,locale,addPartyPinContent,"gold",options)
|
||||
|
||||
let overlayMaps = {}
|
||||
overlayMaps[content.groups] = groupsMarkersLayer
|
||||
@@ -58,14 +85,13 @@
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==6}
|
||||
{#if $loaded==10}
|
||||
<div id="container">
|
||||
<picture>
|
||||
<source srcset="/img/crowd.webp">
|
||||
<source srcset="/img/crowd.png">
|
||||
<img id="crowd" alt="crowd">
|
||||
</picture>
|
||||
|
||||
<div id="text-container">
|
||||
<p>{$content.top}</p>
|
||||
<div bind:this={grid} id="container-grid" style="--grid-width: {gridWidth}">
|
||||
@@ -75,9 +101,9 @@
|
||||
<p>{$content.groupsText}</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href={"/" + locale + "/communes"}><h2>{$content.communesTitle}</h2></a>
|
||||
<img id="communes-img" src="/img/common/commune.svg" alt="communes">
|
||||
<p>{$content.communesText}</p>
|
||||
<a href={"/" + locale + "/parties"}><h2>{$content.partiesTitle}</h2></a>
|
||||
<img id="parties-img" src="/img/common/parties.svg" alt="coops">
|
||||
<p>{$content.partiesText}</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href={"/" + locale + "/coops"}><h2>{$content.cooperativesTitle}</h2></a>
|
||||
@@ -85,13 +111,30 @@
|
||||
<p>{$content.cooperativesText}</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href={"/" + locale + "/parties"}><h2>{$content.partiesTitle}</h2></a>
|
||||
<img id="parties-img" src="/img/common/parties.svg" alt="coops">
|
||||
<p>{$content.partiesText}</p>
|
||||
<a href={"/" + locale + "/communes"}><h2>{$content.communesTitle}</h2></a>
|
||||
<img id="communes-img" src="/img/common/communes.svg" alt="communes">
|
||||
<p>{$content.communesText}</p>
|
||||
</div>
|
||||
</div>
|
||||
<h1>{$content.findUs}</h1>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content)} colors={["#23AC20","#CA2437","#217BC9","#FFD326"]}></map-component>
|
||||
<!--
|
||||
<div id="values-container">
|
||||
<h1>Our Values</h1>
|
||||
<h2>Environmentalism</h2>
|
||||
<p>We are committed to environmental preservation and the urgent need to safeguard our planet's biodiversity. We embrace sustainable practices, such as permaculture, as essential components of our vision for the future. We emphasize the harmonious integration of landscapes, ecosystems, and human settlements. We strive to create resilient and self-sustaining systems that promote biodiversity, enhance soil fertility, conserve water resources, and foster ecological balance.</p>
|
||||
<h2>Against animal exploitation</h2>
|
||||
<p>We embrace a compassionate approach to our relationship with animals, recognizing the inherent value and welfare of all sentient beings. We strive to build a more harmonious coexistence between humans and animals, promoting sustainable and cruelty-free practices that benefit both the planet and its inhabitants. This commitment reflects our deep understanding of the interconnectedness of all living beings.</p>
|
||||
<h2>Queer liberation</h2>
|
||||
<p>We strive for a society free from the constraints and hierarchies imposed by gender or sexual norms. We envision a world where the word “queer” becomes meaningless because individuals are not defined or limited by their assigned gender at birth, where the binary constructs of male and female are dismantled, and where everyone can freely express their authentic selves without fear of discrimination or marginalization.</p>
|
||||
<h2>Racial Justice</h2>
|
||||
<p>We actively challenge and dismantle systemic racism, discrimination, and inequality in all its forms. We advocate for a society that recognizes and celebrates the diversity of human experiences, irrespective of their race or ethnicity. We strive to address the historical and ongoing impacts of racism, dismantle racist structures, and uplift marginalized communities.</p>
|
||||
<h2>Anti-Imperialism</h2>
|
||||
<p>We stand in solidarity with indigenous peoples and support their right to self-determination, land sovereignty, and cultural preservation. We challenge global systems that exploit and subjugate nations and communities.</p>
|
||||
<h2>Peace and non-violence</h2>
|
||||
<p>We promote peaceful resolutions to conflicts and oppose war, militarization, and violence. We advocate for non-violent strategies that address social issues and create lasting positive change.</p>
|
||||
</div>
|
||||
-->
|
||||
<h1 id="find-us">{$content.findUs}</h1>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)} colors={["#23AC20","#CA2437","#217BC9","#FFD326"]}></map-component>
|
||||
<h1>{$content.whatNow}</h1>
|
||||
<div id="action-container">
|
||||
<a class="link-button" href={"/" + locale + "/join-us"}>{$content.joinUs}</a>
|
||||
@@ -105,6 +148,10 @@
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
#values-container {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
#action-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -119,7 +166,7 @@
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
width: 14rem;
|
||||
line-height: 4rem;
|
||||
background: #cb1816;
|
||||
background: var(--red);
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -134,6 +181,7 @@
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#container-grid > div > a > h2 {
|
||||
@@ -184,9 +232,9 @@
|
||||
grid-template-columns: var(--grid-width);
|
||||
grid-template-rows: var(--grid-width);
|
||||
grid-gap: 4rem;
|
||||
row-gap: 3rem;
|
||||
row-gap: 2.5rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#container-grid > div {
|
||||
|
@@ -1,446 +0,0 @@
|
||||
<svelte:options tag="navbar-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { loadLocaleContent, locales } from "/js/libraries/serverTools.js"
|
||||
|
||||
// Main code
|
||||
let hambInput
|
||||
let navbar
|
||||
let localesDropdown;
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let logoText
|
||||
|
||||
let locale = loadLocaleContent(content,"navbar-component",loaded)
|
||||
|
||||
function changeNavbar() {
|
||||
if (hambInput.checked) {
|
||||
navbar.style.background = "white"
|
||||
//navbar.style.boxShadow = "0 0 0.314rem rgb(187, 187, 187)"
|
||||
}
|
||||
else {
|
||||
setTimeout(()=> {
|
||||
navbar.style.position = "relative"
|
||||
navbar.style.background = ""
|
||||
navbar.style.boxShadow = ""
|
||||
}
|
||||
,510)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function showLocales() {
|
||||
if (localesDropdown.style.display=="block") {
|
||||
localesDropdown.style.display = "none"
|
||||
}
|
||||
else {
|
||||
localesDropdown.style.display = "block"
|
||||
}
|
||||
}
|
||||
|
||||
function changeLocale(lang) {
|
||||
localStorage.setItem("locale",lang)
|
||||
let locSplit = location.href.split("/")
|
||||
let localesSymbols = Object.keys(locales)
|
||||
locSplit = locSplit.filter(x => !localesSymbols.includes(x))
|
||||
let loc = locSplit.slice(0,locSplit.length-1).join("/") + "/" + lang + "/" + locSplit[locSplit.length-1]
|
||||
location.href = loc
|
||||
}
|
||||
|
||||
function fixHeading() {
|
||||
if (locale=="ru") {
|
||||
let func = () => {
|
||||
if (logoText==undefined) {
|
||||
setTimeout(func,100)
|
||||
}
|
||||
else {
|
||||
if (window.innerWidth < 400 && logoText.style.lineHeight!="100%") {
|
||||
logoText.style.lineHeight = "120%"
|
||||
logoText.style.top = "1rem"
|
||||
logoText.style.width = "16rem"
|
||||
}
|
||||
else if (window.innerWidth > 400 && logoText.style.lineHeight!="400%") {
|
||||
logoText.style.lineHeight = "400%"
|
||||
logoText.style.top = "0rem"
|
||||
logoText.style.width = "auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
func()
|
||||
addEventListener("resize", func)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Navigation bar -->
|
||||
{#key loaded}
|
||||
{#if Object.keys($content).length!=0}
|
||||
{fixHeading()}{console.log($content)}
|
||||
<header bind:this={navbar} id="navbar">
|
||||
<!-- Hamburger icon -->
|
||||
<input bind:this={hambInput} type="checkbox" id="side-menu" on:click={changeNavbar}>
|
||||
<label id="hamb" for="side-menu"><span id="hamb-line"></span></label>
|
||||
<!-- Logo -->
|
||||
<a id=logo-container href={"/" + locale + "/"}>
|
||||
<img src="/img/common/flag.png" id="navbar-logo" alt="logo">
|
||||
<span bind:this={logoText} id="navbar-logo-text" >{@html $content.orgName}</span>
|
||||
</a>
|
||||
<!-- Menu -->
|
||||
<nav id="nav">
|
||||
<ul id="menu">
|
||||
<li><a href={"/"+locale+"/manifesto"}>{$content.manifesto}</a></li>
|
||||
<li><a href={"/"+locale+"/join-us"}>{$content.joinUs}</a></li>
|
||||
|
||||
<!-- Options dropdown -->
|
||||
<!-- A list of links pointing to different pages of the website. Implemented as a div opened on :hover-->
|
||||
<li id="options-container">
|
||||
<button id="options-button">{$content.aboutUs}</button>
|
||||
<div id="options-dropdown">
|
||||
<ul id="options-list">
|
||||
<li><a href={"/"+locale+"/groups"}>{$content.groups}</a></li>
|
||||
<li><a href={"/"+locale+"/communes"}>{$content.communes}</a></li>
|
||||
|
||||
<li><a href={"/"+locale+"/cooperatives"}>{$content.cooperatives}</a></li>
|
||||
<li><a href={"/"+locale+"/parties"}>{$content.parties}</a></li>
|
||||
<li><a href={"/"+locale+"/partners"}>{$content.partners}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li id="locales">
|
||||
<button on:click={showLocales}>
|
||||
<picture>
|
||||
<source srcset="/img/common/globe.webp">
|
||||
<source srcset="/img/common/globe.png">
|
||||
<img id="locales-img" alt="globe">
|
||||
</picture>
|
||||
</button>
|
||||
</li>
|
||||
<div bind:this={localesDropdown} id="locales-dropdown">
|
||||
{#each Object.entries(locales) as [loc,name]}
|
||||
<button on:click={() => changeLocale(loc)}>{name}</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
/* Header */
|
||||
#navbar{
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: min(100%,116rem);
|
||||
z-index: 1000000000;
|
||||
height: 5.26rem;
|
||||
padding-left: 4rem;
|
||||
padding-right: 4rem;
|
||||
}
|
||||
|
||||
#navbar * {
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
#logo-container {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
margin-left: 1rem;
|
||||
height: 100%;
|
||||
max-height: 5.26rem;
|
||||
color: black;
|
||||
z-index: 1;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#navbar-logo {
|
||||
height: 3.5rem;
|
||||
width: 3.5rem;
|
||||
object-fit: contain;
|
||||
border-radius: 10rem;
|
||||
}
|
||||
|
||||
#navbar-logo-text {
|
||||
position: relative;
|
||||
word-wrap: normal;
|
||||
height: 100%;
|
||||
line-height: 400%;
|
||||
font-size: 1.4rem;
|
||||
color: #292222;
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-weight: 400;
|
||||
padding-left: 1.2rem;
|
||||
}
|
||||
|
||||
/* Nav menu */
|
||||
#nav {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
}
|
||||
#menu a, #options-button {
|
||||
display: block;
|
||||
padding: 1.2rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
color: black;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
#menu a:hover {
|
||||
background-color: rgb(220, 220, 220);
|
||||
}
|
||||
|
||||
#menu a:active{
|
||||
background-color: #f7aec0;
|
||||
}
|
||||
|
||||
#menu li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#nav{
|
||||
max-height: 0;
|
||||
/*transition: max-height .5s ease-out;*/
|
||||
}
|
||||
|
||||
/* Menu Icon */
|
||||
#hamb{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
right: 0rem;
|
||||
padding: 2.8rem 2rem;
|
||||
z-index: 9999;
|
||||
}/* Style label tag */
|
||||
|
||||
#hamb-line {
|
||||
background: black;
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: relative;
|
||||
width: 24px;
|
||||
|
||||
} /* Style span tag */
|
||||
|
||||
#hamb-line::before,
|
||||
#hamb-line::after{
|
||||
background: black;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: all .2s ease-out;
|
||||
width: 100%;
|
||||
}
|
||||
#hamb-line::before{
|
||||
top: 5px;
|
||||
}
|
||||
#hamb-line::after{
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
#side-menu {
|
||||
display: none;
|
||||
} /* Hide checkbox */
|
||||
|
||||
/* Toggle menu icon */
|
||||
#side-menu:checked ~ nav {
|
||||
display: block;
|
||||
max-height: 100%;
|
||||
padding-top: 5.625rem;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #logo-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #logo-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line::before {
|
||||
transform: rotate(-45deg);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line::after {
|
||||
transform: rotate(45deg);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Options */
|
||||
|
||||
#options-dropdown {
|
||||
position: absolute;
|
||||
display: none;
|
||||
border: #404040 solid 0.1rem;
|
||||
/*padding: 0.5rem;*/
|
||||
background-color: white;
|
||||
overflow: auto;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
#options-list > li a {
|
||||
padding: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
#options-list > li:hover {
|
||||
background-color: rgb(187 53 52 / 96%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#options-list > li:hover > a{
|
||||
color: white;
|
||||
}
|
||||
|
||||
#options-container:hover #options-dropdown {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*Localization*/
|
||||
|
||||
#locales {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#locales button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
#locales button:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#locales-img {
|
||||
position: relative;
|
||||
top: 0rem;
|
||||
height: 2rem;
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
|
||||
#locales-dropdown {
|
||||
position: absolute;
|
||||
display: none;
|
||||
top: 5.6rem;
|
||||
right: 1.8rem;
|
||||
border: #404040 solid 0.1rem;
|
||||
padding: 1.4rem;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#locales-dropdown button {
|
||||
display: block;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#locales-dropdown button:hover {
|
||||
color: rgb(127, 127, 127);
|
||||
}
|
||||
|
||||
#locales-dropdown>:first-child {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#locales-dropdown>:nth-child(2) {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
/* Responsiveness */
|
||||
@media only screen and (min-width: 1500px) {
|
||||
|
||||
#navbar {
|
||||
position: relative;
|
||||
width: min(100%,116rem);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding-right: 4rem;
|
||||
padding-left: 4rem;
|
||||
}
|
||||
|
||||
#nav {
|
||||
max-height: none;
|
||||
top: 0;
|
||||
position: relative;
|
||||
float: right;
|
||||
width: fit-content;
|
||||
background-color: transparent;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ nav {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#menu li {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#menu a:hover, #options-button:hover {
|
||||
background-color: transparent;
|
||||
color: rgb(127, 127, 127);
|
||||
}
|
||||
|
||||
#menu a, #options-button {
|
||||
padding: 1.2rem;
|
||||
padding-top: 1.9rem;
|
||||
padding-bottom: 1.9rem;
|
||||
}
|
||||
|
||||
#hamb {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#locales {
|
||||
position: relative;
|
||||
margin-right: 1.8rem;
|
||||
}
|
||||
|
||||
#locales-img {
|
||||
top: 0.9rem;
|
||||
}
|
||||
|
||||
#locales-dropdown {
|
||||
top: 5,7rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
151
Server/app/svelte/src/navbar/navbar-logged.svelte
Normal file
151
Server/app/svelte/src/navbar/navbar-logged.svelte
Normal file
@@ -0,0 +1,151 @@
|
||||
<svelte:options tag="navbar-logged" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { loadLocaleContent, locales } from "/js/libraries/serverTools.js"
|
||||
|
||||
// Main code
|
||||
let hambInput
|
||||
let navbar
|
||||
let localesDropdown
|
||||
let initiativesDropdown
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let logoText
|
||||
|
||||
let locale = loadLocaleContent(content,"navbar-component",loaded)
|
||||
|
||||
function changeNavbar() {
|
||||
if (hambInput.checked) {
|
||||
navbar.style.background = "white"
|
||||
//navbar.style.boxShadow = "0 0 0.314rem rgb(187, 187, 187)"
|
||||
}
|
||||
else {
|
||||
setTimeout(()=> {
|
||||
navbar.style.position = "relative"
|
||||
navbar.style.background = ""
|
||||
navbar.style.boxShadow = ""
|
||||
}
|
||||
,510)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function showDropdown(dropdown) {
|
||||
let state = dropdown.style.display
|
||||
initiativesDropdown.style.display = "none"
|
||||
localesDropdown.style.display = "none"
|
||||
if (state=="block") {
|
||||
dropdown.style.display = "none"
|
||||
}
|
||||
else {
|
||||
dropdown.style.display = "block"
|
||||
}
|
||||
}
|
||||
|
||||
function changeLocale(lang) {
|
||||
localStorage.setItem("locale",lang)
|
||||
let locSplit = location.href.split("/")
|
||||
let localesSymbols = Object.keys(locales)
|
||||
locSplit = locSplit.filter(x => !localesSymbols.includes(x))
|
||||
let loc = locSplit.slice(0,locSplit.length-1).join("/") + "/" + lang + "/" + locSplit[locSplit.length-1]
|
||||
location.href = loc
|
||||
}
|
||||
|
||||
function fixHeading() {
|
||||
if (locale=="ru") {
|
||||
let func = () => {
|
||||
if (logoText==undefined) {
|
||||
setTimeout(func,100)
|
||||
}
|
||||
else {
|
||||
if (((window.innerWidth < 1700 && window.innerWidth > 1400) || window.innerWidth < 400) && logoText.style.lineHeight!="100%") {
|
||||
logoText.style.lineHeight = "120%"
|
||||
logoText.style.top = "1rem"
|
||||
logoText.style.width = "16rem"
|
||||
}
|
||||
else if (((window.innerWidth > 1700) || (window.innerWidth > 400 && window.innerWidth < 1400)) && logoText.style.lineHeight!="400%") {
|
||||
logoText.style.lineHeight = "400%"
|
||||
logoText.style.top = "0rem"
|
||||
logoText.style.width = "auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
func()
|
||||
addEventListener("resize", func)
|
||||
}
|
||||
}
|
||||
|
||||
function hide(dropdown) {
|
||||
let callback = () => {
|
||||
dropdown.style.display = "none"
|
||||
}
|
||||
setTimeout(callback,100)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fixHeading()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Navigation bar -->
|
||||
{#key loaded}
|
||||
{#if Object.keys($content).length!=0}
|
||||
<header bind:this={navbar} id="navbar">
|
||||
<!-- Hamburger icon -->
|
||||
<input bind:this={hambInput} type="checkbox" id="side-menu" on:click={changeNavbar}>
|
||||
<label id="hamb" for="side-menu"><span id="hamb-line"></span></label>
|
||||
<!-- Logo -->
|
||||
<a id=logo-container href={"/" + locale + "/"}>
|
||||
<img src="/img/common/flag.png" id="navbar-logo" alt="logo">
|
||||
<span bind:this={logoText} id="navbar-logo-text" >{@html $content.orgName}</span>
|
||||
</a>
|
||||
<!-- Menu -->
|
||||
<nav id="nav">
|
||||
<ul id="menu">
|
||||
<li><a href={"/"+locale+"/join-us"}>{$content.joinUs}</a></li>
|
||||
<li><a href={"/"+locale+"/manifesto"}>{$content.manifesto}</a></li>
|
||||
<!-- Options dropdown -->
|
||||
<!-- A list of links pointing to different pages of the website. Implemented as a div opened on :hover-->
|
||||
<li id="options-container">
|
||||
<button on:click={() => showDropdown(initiativesDropdown)} on:focusout={() => hide(initiativesDropdown)} class="options-button">{$content.initiatives}</button>
|
||||
<div bind:this={initiativesDropdown} class="options-dropdown">
|
||||
<a href={"/"+locale+"/groups"}>{$content.groups}</a>
|
||||
<a href={"/"+locale+"/communes"}>{$content.communes}</a>
|
||||
<a href={"/"+locale+"/cooperatives"}>{$content.cooperatives}</a>
|
||||
<a href={"/"+locale+"/parties"}>{$content.parties}</a>
|
||||
<a href={"/"+locale+"/partners"}>{$content.partners}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li><a href={"/"+locale+"/profile"}>{$content.profile}</a></li>
|
||||
<li id="locales">
|
||||
<button on:click={() => showDropdown(localesDropdown)} on:focusout={() => hide(localesDropdown)}>
|
||||
<picture>
|
||||
<source srcset="/img/common/globe.webp">
|
||||
<source srcset="/img/common/globe.png">
|
||||
<img id="locales-img" alt="globe">
|
||||
</picture>
|
||||
</button>
|
||||
</li>
|
||||
<div bind:this={localesDropdown} class="options-dropdown">
|
||||
{#each Object.entries(locales) as [loc,name]}
|
||||
<button on:click={() => changeLocale(loc)}>{name}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
@import '/css/navbar.css';
|
||||
|
||||
</style>
|
151
Server/app/svelte/src/navbar/navbar-not-logged.svelte
Normal file
151
Server/app/svelte/src/navbar/navbar-not-logged.svelte
Normal file
@@ -0,0 +1,151 @@
|
||||
<svelte:options tag="navbar-not-logged" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { loadLocaleContent, locales } from "/js/libraries/serverTools.js"
|
||||
|
||||
// Main code
|
||||
let hambInput
|
||||
let navbar
|
||||
let localesDropdown
|
||||
let initiativesDropdown
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let logoText
|
||||
|
||||
let locale = loadLocaleContent(content,"navbar-component",loaded)
|
||||
|
||||
function changeNavbar() {
|
||||
if (hambInput.checked) {
|
||||
navbar.style.background = "white"
|
||||
//navbar.style.boxShadow = "0 0 0.314rem rgb(187, 187, 187)"
|
||||
}
|
||||
else {
|
||||
setTimeout(()=> {
|
||||
navbar.style.position = "relative"
|
||||
navbar.style.background = ""
|
||||
navbar.style.boxShadow = ""
|
||||
}
|
||||
,510)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function showDropdown(dropdown) {
|
||||
let state = dropdown.style.display
|
||||
initiativesDropdown.style.display = "none"
|
||||
localesDropdown.style.display = "none"
|
||||
if (state=="block") {
|
||||
dropdown.style.display = "none"
|
||||
}
|
||||
else {
|
||||
dropdown.style.display = "block"
|
||||
}
|
||||
}
|
||||
|
||||
function changeLocale(lang) {
|
||||
localStorage.setItem("locale",lang)
|
||||
let locSplit = location.href.split("/")
|
||||
let localesSymbols = Object.keys(locales)
|
||||
locSplit = locSplit.filter(x => !localesSymbols.includes(x))
|
||||
let loc = locSplit.slice(0,locSplit.length-1).join("/") + "/" + lang + "/" + locSplit[locSplit.length-1]
|
||||
location.href = loc
|
||||
}
|
||||
|
||||
function fixHeading() {
|
||||
if (locale=="ru") {
|
||||
let func = () => {
|
||||
if (logoText==undefined) {
|
||||
setTimeout(func,100)
|
||||
}
|
||||
else {
|
||||
if (((window.innerWidth < 1700 && window.innerWidth > 1400) || window.innerWidth < 400) && logoText.style.lineHeight!="100%") {
|
||||
logoText.style.lineHeight = "120%"
|
||||
logoText.style.top = "1rem"
|
||||
logoText.style.width = "16rem"
|
||||
}
|
||||
else if (((window.innerWidth > 1700) || (window.innerWidth > 400 && window.innerWidth < 1400)) && logoText.style.lineHeight!="400%") {
|
||||
logoText.style.lineHeight = "400%"
|
||||
logoText.style.top = "0rem"
|
||||
logoText.style.width = "auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
func()
|
||||
addEventListener("resize", func)
|
||||
}
|
||||
}
|
||||
|
||||
function hide(dropdown) {
|
||||
let callback = () => {
|
||||
dropdown.style.display = "none"
|
||||
}
|
||||
setTimeout(callback,100)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fixHeading()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Navigation bar -->
|
||||
{#key loaded}
|
||||
{#if Object.keys($content).length!=0}
|
||||
<header bind:this={navbar} id="navbar">
|
||||
<!-- Hamburger icon -->
|
||||
<input bind:this={hambInput} type="checkbox" id="side-menu" on:click={changeNavbar}>
|
||||
<label id="hamb" for="side-menu"><span id="hamb-line"></span></label>
|
||||
<!-- Logo -->
|
||||
<a id=logo-container href={"/" + locale + "/"}>
|
||||
<img src="/img/common/flag.png" id="navbar-logo" alt="logo">
|
||||
<span bind:this={logoText} id="navbar-logo-text" >{@html $content.orgName}</span>
|
||||
</a>
|
||||
<!-- Menu -->
|
||||
<nav id="nav">
|
||||
<ul id="menu">
|
||||
<li><a href={"/"+locale+"/join-us"}>{$content.joinUs}</a></li>
|
||||
<li><a href={"/"+locale+"/manifesto"}>{$content.manifesto}</a></li>
|
||||
<!-- Options dropdown -->
|
||||
<!-- A list of links pointing to different pages of the website. Implemented as a div opened on :hover-->
|
||||
<li id="options-container">
|
||||
<button on:click={() => showDropdown(initiativesDropdown)} on:focusout={() => hide(initiativesDropdown)} class="options-button">{$content.initiatives}</button>
|
||||
<div bind:this={initiativesDropdown} class="options-dropdown">
|
||||
<a href={"/"+locale+"/groups"}>{$content.groups}</a>
|
||||
<a href={"/"+locale+"/communes"}>{$content.communes}</a>
|
||||
<a href={"/"+locale+"/cooperatives"}>{$content.cooperatives}</a>
|
||||
<a href={"/"+locale+"/parties"}>{$content.parties}</a>
|
||||
<a href={"/"+locale+"/partners"}>{$content.partners}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li><a href={"/"+locale+"/auth"}>{$content.login}</a></li>
|
||||
<li id="locales">
|
||||
<button on:click={() => showDropdown(localesDropdown)} on:focusout={() => hide(localesDropdown)}>
|
||||
<picture>
|
||||
<source srcset="/img/common/globe.webp">
|
||||
<source srcset="/img/common/globe.png">
|
||||
<img id="locales-img" alt="globe">
|
||||
</picture>
|
||||
</button>
|
||||
</li>
|
||||
<div bind:this={localesDropdown} class="options-dropdown">
|
||||
{#each Object.entries(locales) as [loc,name]}
|
||||
<button on:click={() => changeLocale(loc)}>{name}</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
@import '/css/navbar.css';
|
||||
</style>
|
413
Server/app/svelte/src/parties-add-component.svelte
Normal file
413
Server/app/svelte/src/parties-add-component.svelte
Normal file
@@ -0,0 +1,413 @@
|
||||
<svelte:options tag="parties-add-component" />
|
||||
|
||||
<script>
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { loadLocaleContent, getData, sendData } from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/parties.json",callback)
|
||||
|
||||
|
||||
let confirmationMsg
|
||||
let addressInput
|
||||
let contactInput
|
||||
let addressVec
|
||||
let userPinLat = 0
|
||||
let userPinLng = 0
|
||||
let userPin = createPin(0,0)
|
||||
userPin.setOpacity(0)
|
||||
|
||||
let locale = loadLocaleContent(content,"parties-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
function createPin(lat,lng) {
|
||||
let markerIcon = new L.Icon({
|
||||
iconUrl: '/img/common/markers/marker-black.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
})
|
||||
return L.marker([lat,lng], {icon: markerIcon})
|
||||
}
|
||||
|
||||
function updatePin(marker,lat,lng) {
|
||||
let newLatLng = L.latLng(lat, lng); // Replace with the desired coordinates
|
||||
marker.setLatLng(newLatLng)
|
||||
}
|
||||
|
||||
function reverseGeocodeLocal(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressInput.value = fullAddress
|
||||
resizeInput(addressInput)
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function reverseGeocode(latitude, longitude) {
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=jsonv2&accept-language=en`;
|
||||
|
||||
let callback = (response) => {
|
||||
// Parse the response JSON
|
||||
response = JSON.parse(response)
|
||||
// Extract the address information from the response
|
||||
let address = response.address
|
||||
let city = address.city || address.town || address.village || address.hamlet
|
||||
let state = address.state
|
||||
let country = address.country
|
||||
let fullAddress = country
|
||||
if (state!=undefined) {
|
||||
fullAddress += ", " + state
|
||||
}
|
||||
else {
|
||||
state = ""
|
||||
}
|
||||
if (city!=undefined) {
|
||||
fullAddress += ", " + city
|
||||
}
|
||||
else {
|
||||
city = ""
|
||||
}
|
||||
addressVec = [country,state,city]
|
||||
}
|
||||
getData(url,callback)
|
||||
}
|
||||
|
||||
function addGroupPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field of ["location","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green")
|
||||
|
||||
userPin.addTo(map)
|
||||
map.on('click', function(event) {
|
||||
let lat = event.latlng.lat;
|
||||
let lng = event.latlng.lng;
|
||||
userPinLat = lat
|
||||
userPinLng = lng
|
||||
updatePin(userPin,lat,lng)
|
||||
userPin.setOpacity(1)
|
||||
reverseGeocodeLocal(lat, lng)
|
||||
reverseGeocode(lat, lng)
|
||||
})
|
||||
}
|
||||
|
||||
function updateConfirmationMsg(response) {
|
||||
if (response!==false) {
|
||||
|
||||
confirmationMsg.innerHTML = "You have been added to our database! Now go to our Discord to verify yourself."
|
||||
confirmationMsg.style.color = "green"
|
||||
}
|
||||
else {
|
||||
confirmationMsg.innerHTML = "Something went wrong."
|
||||
confirmationMsg.style.color = "red"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function submitLocation() {
|
||||
if (addressVec!=undefined) {
|
||||
let data = {
|
||||
country: addressVec[0],
|
||||
state: addressVec[1],
|
||||
town: addressVec[2],
|
||||
latitude: userPinLat,
|
||||
longitude: userPinLng,
|
||||
contact: contactInput.value
|
||||
}
|
||||
|
||||
if (data.state=="") {
|
||||
data.state = null
|
||||
}
|
||||
if (data.town=="") {
|
||||
data.town = null
|
||||
}
|
||||
if (data.contact=="") {
|
||||
data.contact = null
|
||||
}
|
||||
let url = "/" + locale + "/parties-add-post/"
|
||||
sendData(url,data,updateConfirmationMsg)
|
||||
}
|
||||
}
|
||||
|
||||
function resizeInput(el) {
|
||||
el.nextElementSibling.innerHTML = el.value
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
<h1>Add a Party</h1>
|
||||
<img id="parties-img" src="/img/common/parties.svg" alt="parties">
|
||||
<p class="description">If there are no parties in your country which you can join then do the following:</p>
|
||||
<ol>
|
||||
<li>Click on the map to show us where you are located;</li>
|
||||
<li>Add a way to contact you or leave blank for a pin to point to our discord;</li>
|
||||
<li>Press "Submit" to add yourself to our map;</li>
|
||||
<li>Verify yourself by having a chat with us at our Discord server to show on the map;</li>
|
||||
</ol>
|
||||
<div id="address-input-wrapper" class="input-label-wrapper">
|
||||
<label for="address-input">Location: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={addressInput} on:input={() => resizeInput(addressInput)} id="address-input" type="text" readonly>
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-label-wrapper">
|
||||
<label for="contact-input">Contact: </label>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={contactInput} on:input={() => resizeInput(contactInput)} id="contact-input" type="text">
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="submit-button" on:click={submitLocation}>Submit</button>
|
||||
<p id="confirmation-msg" bind:this={confirmationMsg}></p>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
#confirmation-msg {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin-left: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
font-family: var(--serif,serif);
|
||||
font-size: 1.15rem;
|
||||
line-height: 160%;
|
||||
color: #222222;
|
||||
width: 5.5rem;
|
||||
}
|
||||
|
||||
input, .ghost-input {
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--serif,serif);
|
||||
}
|
||||
|
||||
input {
|
||||
height: 2.5rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#address-input, #contact-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#address-input-wrapper {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ghost-input {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 5.5rem);
|
||||
min-width: min(20rem, calc(100% - 5.5rem));
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.input-label-wrapper {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.input-label-wrapper label {
|
||||
position: relative;
|
||||
top: 0.3rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#submit-button {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-top: 2rem;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.4rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
border: 0rem solid black;
|
||||
border-radius: 0.5rem;
|
||||
background: #cb1816;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#add-prompt {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#parties-img {
|
||||
position: absolute;
|
||||
width: 14rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
z-index: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
#text-container>:nth-child(3) {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
.country-name {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.country-block {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.location-info {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.location-info p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #DD1C1A;
|
||||
}
|
||||
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-top: 2rem;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
position: relative;
|
||||
max-width: calc(100vw - 4rem);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 2.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
#container p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
</style>
|
@@ -4,8 +4,9 @@
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { partiesByCountry, addMarkersParties } from '/js/parties.js'
|
||||
import { loadLocaleContent } from "/js/libraries/serverTools.js"
|
||||
import { loadLocaleContent, getData} from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addPartyPinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -13,13 +14,45 @@
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
let locale = loadLocaleContent(content,"parties-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
loadLocaleContent(content,"parties-component",loaded)
|
||||
|
||||
function mapCallbackParties(createMap,content) {
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/parties.json",callback)
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersParties(map,content)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addPartyPinContent,"gold")
|
||||
}
|
||||
|
||||
function getCountry(x) {
|
||||
return locale=="en" ? x : translate($content,x)
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -28,7 +61,7 @@
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==2}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
@@ -36,26 +69,27 @@
|
||||
<img id="party-img" src="/img/common/parties.svg" alt="party">
|
||||
<p class="description">{$content.p1}</p>
|
||||
<h3>{$content.subheading1}</h3>
|
||||
<map-component id="map" callback={(createMap) => mapCallbackParties(createMap,$content)}></map-component>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
<p id="add-prompt">{$content["map-prompt"]}</p>
|
||||
{#each Object.entries(partiesByCountry) as [name,parties]}
|
||||
<h4 class="country-name">{$content[name]}</h4>
|
||||
{#each Object.entries(entriesByCountry) as [name,entries]}
|
||||
<h4 class="country-name">{getCountry(name)}</h4>
|
||||
<div class="country-block">
|
||||
{#each parties as party}
|
||||
{#each entries as entry}
|
||||
<div class="location-info">
|
||||
<div class="img-general-info">
|
||||
<picture>
|
||||
<source srcset={"/img/parties/"+party.logo+".webp"}>
|
||||
<source srcset={"/img/parties/"+party.logo+".jpg"}>
|
||||
<source srcset={"/img/parties/"+entry.logo+".webp"}>
|
||||
<source srcset={"/img/parties/"+entry.logo+".jpg"}>
|
||||
<img class="party-logo" alt="logo">
|
||||
</picture>
|
||||
<div>
|
||||
<p><b>{$content.name}: </b>{party.name}</p>
|
||||
<p><b>{$content.location}: </b>{$content[party.location[0]]}</p>
|
||||
<p><b>{$content.link}: </b><a href={party.link} target=;_blank; rel=noreferrer>{party.link}</a></p>
|
||||
<p><b>{$content.name}: </b>{entry.name}</p>
|
||||
<p><b>{$content.location}: </b>{getAddress(entry)}</p>
|
||||
<p><b>{$content.website}: </b><a href={entry.website} target=;_blank; rel=noreferrer>{entry.website}</a></p>
|
||||
<p><b>{$content.contact}: </b><a href={entry.contact} target=;_blank; rel=noreferrer>{entry.contact}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<p><b>{$content.description}: </b>{$content[party.description]}</p>
|
||||
<p><b>{$content.description}: </b>{entry.description}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -126,7 +160,7 @@
|
||||
.party-logo {
|
||||
position: relative;
|
||||
right: 0;
|
||||
max-height: 5.5rem;
|
||||
max-height: 6.5rem;
|
||||
max-width: 100%;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
195
Server/app/svelte/src/partners-add-component.svelte
Normal file
195
Server/app/svelte/src/partners-add-component.svelte
Normal file
@@ -0,0 +1,195 @@
|
||||
<svelte:options tag="partners-component" />
|
||||
|
||||
<script>
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { loadLocaleContent, getData} from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
let locale = loadLocaleContent(content,"groups-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/groups.json",callback)
|
||||
|
||||
function addGroupPinContent(g,content,locale) {
|
||||
let coordinates
|
||||
let text = "<b>"+content["Group"]+"</b><br>"
|
||||
for (let field of ["location","members","contact"]) {
|
||||
let fieldText = content[field] + ": "
|
||||
if (field=="contact") {
|
||||
text += fieldText + "<a href='" + g.contact + "' target='_blank' rel=noreferrer>" + g.contact + "</a>"
|
||||
}
|
||||
else if (field=="location") {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null && x!=undefined)
|
||||
let locationString
|
||||
if (locale=="en") {
|
||||
locationString = location.map(x => x).join(", ")
|
||||
}
|
||||
else {
|
||||
locationString = location.map(x => translate(content, x)).join(", ")
|
||||
}
|
||||
text += fieldText + locationString + "<br>"
|
||||
coordinates = [g.latitude,g.longitude]
|
||||
}
|
||||
else {
|
||||
text += fieldText + g[field] + "<br>"
|
||||
}
|
||||
}
|
||||
return {text,coordinates}
|
||||
}
|
||||
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green")
|
||||
}
|
||||
|
||||
function getCountry(x) {
|
||||
return locale=="en" ? x : translate($content,x)
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
<h1>{$content.groups}</h1>
|
||||
<img id="groups-img" src="/img/common/groups.svg" alt="groups">
|
||||
<p class="description">{$content.p1}</p>
|
||||
<h3>{$content.subheading1}</h3>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
<p id="add-prompt">{$content["map-prompt"]}</p>
|
||||
{#each Object.entries(entriesByCountry) as [name,entries]}
|
||||
<h4 class="country-name">{getCountry(name)}</h4>
|
||||
<div class="country-block">
|
||||
{#each entries as entry}
|
||||
<div class="location-info">
|
||||
<p><b>{$content.location}: </b>{getAddress(entry)}</p>
|
||||
<p><b>{$content.members}: </b>{entry.members}</p>
|
||||
<p><b>{$content.contact}: </b><a href={entry.contact} target=;_blank; rel=noreferrer>{entry.contact}</a></p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
.description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#add-prompt {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#groups-img {
|
||||
position: absolute;
|
||||
width: 14rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
z-index: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
#text-container>:nth-child(3) {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
.country-name {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.country-block {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.location-info {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.location-info p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #DD1C1A;
|
||||
}
|
||||
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
position: relative;
|
||||
max-width: calc(100vw - 4rem);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 2.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
#container p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
</style>
|
@@ -4,8 +4,9 @@
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store';
|
||||
import { loadLocaleContent } from "/js/libraries/serverTools.js"
|
||||
import { partnersByCountry } from '/js/partners.js'
|
||||
import { loadLocaleContent, getData} from "/js/libraries/serverTools.js"
|
||||
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
|
||||
import { addPartnersPinContent } from "/js/mapFuncs.js"
|
||||
|
||||
// Import components
|
||||
import "/js/components/map-component.js"
|
||||
@@ -13,9 +14,46 @@
|
||||
// Main code
|
||||
let loaded = writable(0)
|
||||
let content = writable({})
|
||||
let entries
|
||||
let entriesByCountry
|
||||
|
||||
let locale = loadLocaleContent(content,"partners-component",loaded)
|
||||
loadLocaleContent(content,"countries",loaded)
|
||||
loadLocaleContent(content,"partners-component",loaded)
|
||||
|
||||
let callback = (response) => {
|
||||
entries = JSON.parse(response)
|
||||
entriesByCountry = {}
|
||||
for (let g of entries) {
|
||||
let country = g.country
|
||||
if (g.contact==null) {
|
||||
g.contact = "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
if (country in entriesByCountry) {
|
||||
entriesByCountry[country].push(g)
|
||||
}
|
||||
else {
|
||||
entriesByCountry[country] = [g]
|
||||
}
|
||||
}
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/assets/partners.json",callback)
|
||||
|
||||
function mapCallback(createMap,content,locale) {
|
||||
let map = createMap([22, 0],2)
|
||||
addMarkersEntries(entries,entriesByCountry,map,content,locale,addPartnersPinContent,"blue")
|
||||
}
|
||||
|
||||
function getCountry(x) {
|
||||
return locale=="en" ? x : translate($content,x)
|
||||
}
|
||||
|
||||
function getAddress(g) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
@@ -23,7 +61,7 @@
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==2}
|
||||
{#if $loaded==3}
|
||||
<div id="container">
|
||||
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
|
||||
<div id="text-container">
|
||||
@@ -31,25 +69,27 @@
|
||||
<img id="hands-img" src="/img/common/handshake.svg" alt="hands">
|
||||
<p>{$content.p1}</p>
|
||||
<h3>{$content.subheading1}</h3>
|
||||
{#each Object.entries(partnersByCountry) as [name,partners]}
|
||||
<h4 class="country-name">{$content[name]}</h4>
|
||||
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
|
||||
<p id="add-prompt">{$content["map-prompt"]}</p>
|
||||
{#each Object.entries(entriesByCountry) as [name,entries]}
|
||||
<h4 class="country-name">{getCountry(name)}</h4>
|
||||
<div class="country-block">
|
||||
{#each partners as partner}
|
||||
{#each entries as entry}
|
||||
<div class="location-info">
|
||||
<div class="img-general-info">
|
||||
<picture>
|
||||
<source srcset={"/img/partners/"+partner.logo+".webp"}>
|
||||
<source srcset={"/img/partners/"+partner.logo+".jpg"}>
|
||||
<source srcset={"/img/partners/"+entry.logo+".webp"}>
|
||||
<source srcset={"/img/partners/"+entry.logo+".jpg"}>
|
||||
<img class="partner-logo" alt="logo">
|
||||
</picture>
|
||||
<div>
|
||||
<p><b>{$content.name}: </b>{partner.name}</p>
|
||||
<p><b>{$content.type}: </b>{$content[partner.type]}</p>
|
||||
<p><b>{$content.location}: </b>{$content[partner.location[0][0]] + (partner.location[0][1]=="" ? "" : ", " + $content[partner.location[0][1]])}</p>
|
||||
<p><b>{$content.link}: </b><a href={partner.link} target=;_blank; rel=noreferrer>{partner.link}</a></p>
|
||||
<p><b>{$content.name}: </b>{entry.name}</p>
|
||||
<p><b>{$content.location}: </b>{getAddress(entry)}</p>
|
||||
<p><b>{$content.website}: </b><a href={entry.website} target=;_blank; rel=noreferrer>{entry.website}</a></p>
|
||||
<p><b>{$content.contact}: </b><a href={entry.website} target=;_blank; rel=noreferrer>{entry.contact}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<p><b>{$content.description}: </b>{$content[partner.description]}</p>
|
||||
<p><b>{$content.description}: </b>{entry.description}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -125,7 +165,7 @@
|
||||
#map {
|
||||
--height: 30rem;
|
||||
--width: 100%;
|
||||
--margin-bottom: 3rem;
|
||||
--margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#text-container {
|
||||
@@ -140,7 +180,7 @@
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#container {
|
||||
|
29
Server/app/svelte/src/profile/profile-communes.svelte
Normal file
29
Server/app/svelte/src/profile/profile-communes.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<svelte:options tag="profile-communes" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
// Main code
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<h3>Under development</h3>
|
||||
|
||||
<p style=" position: relative; margin-top: 2rem;">Visit <a href="https://discord.gg/Qk8KUk787z" style="color: #c52a28;">https://discord.gg/Qk8KUk787z</a> and ask for your commune to be added.</p>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
284
Server/app/svelte/src/profile/profile-component.svelte
Normal file
284
Server/app/svelte/src/profile/profile-component.svelte
Normal file
@@ -0,0 +1,284 @@
|
||||
<svelte:options tag="profile-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { onMount, afterUpdate, setContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
import {svgFromObject} from "/js/libraries/miscTools.js"
|
||||
|
||||
//Import components
|
||||
import "/js/components/pane-aligner.js"
|
||||
import "/js/components/profile-general.js"
|
||||
import "/js/components/profile-groups.js"
|
||||
import "/js/components/profile-communes.js"
|
||||
import "/js/components/profile-coops.js"
|
||||
import "/js/components/profile-parties.js"
|
||||
import "/js/components/groups-add-component.js"
|
||||
|
||||
// Main code
|
||||
AuthTools.redirectNotLogged()
|
||||
|
||||
let root
|
||||
let general
|
||||
let groups
|
||||
let communes
|
||||
let coops
|
||||
let parties
|
||||
let panes
|
||||
let groupsAdd
|
||||
|
||||
let generalButton
|
||||
let groupsButton
|
||||
let communesButton
|
||||
let coopsButton
|
||||
let partiesButton
|
||||
let buttons
|
||||
|
||||
let currentPaneIndex = 0
|
||||
|
||||
let locationPopup
|
||||
|
||||
let maps = {}
|
||||
|
||||
let user = {}
|
||||
let loaded = writable(0)
|
||||
let reloadTriggerVal = writable(0)
|
||||
AuthTools.getUser(user,loaded)
|
||||
|
||||
function changePane(pane,button) {
|
||||
for (let p of panes) {
|
||||
p.style.display = "none"
|
||||
}
|
||||
for (let b of buttons) {
|
||||
styleField(b,400,"#636363")
|
||||
}
|
||||
pane.style.display = "initial"
|
||||
|
||||
styleField(button,500,"#c52a28")
|
||||
}
|
||||
|
||||
function styleField(div,weight,color) {
|
||||
let svgObject = div.querySelector("object")
|
||||
if (svgObject==null) {
|
||||
let f = () => styleField(div,weight,color)
|
||||
setTimeout(f,100)
|
||||
}
|
||||
else {
|
||||
let svgItem = svgFromObject(svgObject)
|
||||
if (svgItem==null) {
|
||||
let f = () => styleField(div,weight,color)
|
||||
setTimeout(f,100)
|
||||
}
|
||||
else {
|
||||
div.style.fontWeight = weight
|
||||
svgItem.setAttribute("fill", color)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function fillFields() {
|
||||
if (Object.keys(user).length!=0 && root!=undefined) {
|
||||
for (let b of buttons) {
|
||||
styleField(b,400,"#636363")
|
||||
}
|
||||
styleField(buttons[currentPaneIndex],500,"#c52a28")
|
||||
}
|
||||
else {
|
||||
setTimeout(fillFields, 100)
|
||||
}
|
||||
}
|
||||
|
||||
function valid(el) {
|
||||
return (el!=undefined) && (el!=null)
|
||||
}
|
||||
|
||||
function init() {
|
||||
panes = [general,groups,communes,coops,parties]
|
||||
buttons = [generalButton,groupsButton,communesButton,coopsButton,partiesButton]
|
||||
if ($loaded==1 && panes.every(x => valid(x)) && buttons.every(x => valid(x))) {
|
||||
panes = [general,groups,communes,coops,parties]
|
||||
buttons = [generalButton,groupsButton,communesButton,coopsButton,partiesButton]
|
||||
|
||||
fillFields()
|
||||
general.style.display = "initial"
|
||||
}
|
||||
else {
|
||||
let f = () => init()
|
||||
setTimeout(f,100)
|
||||
}
|
||||
}
|
||||
|
||||
function reloadTrigger() {
|
||||
reloadTriggerVal.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
|
||||
setContext("profile-component",{user,maps,reloadTrigger})
|
||||
|
||||
onMount(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<div bind:this={locationPopup} class="overlay" style="display: none">
|
||||
<div id="location-overlay-content">
|
||||
|
||||
</div>
|
||||
<button class="overlay-button" on:click={() => locationPopup.style.display = "none"}></button>
|
||||
</div>
|
||||
-->
|
||||
<pane-aligner>
|
||||
<div id="left-column" class="pane" slot="sidebar-left" bind:this={root}>
|
||||
<button bind:this={generalButton} on:click={() => changePane(general,generalButton)}>
|
||||
<object id="general-img" class="icons" type="image/svg+xml" data="/img/profile/icons/general.svg" title="general"></object>
|
||||
<span>general</span>
|
||||
</button>
|
||||
<button bind:this={groupsButton} on:click={() => changePane(groups,groupsButton)}>
|
||||
<object id="groups-img" class="icons" type="image/svg+xml" data="/img/common/groups.svg" title="groups"></object>
|
||||
<span>groups</span>
|
||||
</button>
|
||||
<button bind:this={communesButton} on:click={() => changePane(communes,communesButton)}>
|
||||
<object id="communes-img" class="icons" type="image/svg+xml" data="/img/common/communes.svg" title="communes"></object>
|
||||
<span>communes</span>
|
||||
</button>
|
||||
<button bind:this={coopsButton} on:click={() => changePane(coops,coopsButton)}>
|
||||
<object id="coops-img" class="icons" type="image/svg+xml" data="/img/common/coops.svg" title="coops"></object>
|
||||
<span>cooperatives</span>
|
||||
</button>
|
||||
<button bind:this={partiesButton} on:click={() => changePane(parties,partiesButton)}>
|
||||
<object id="parties-img" class="icons" type="image/svg+xml" data="/img/common/parties.svg" title="parties"></object>
|
||||
<span>parties</span>
|
||||
</button>
|
||||
<button on:click={AuthTools.logout} id="logout-button">
|
||||
<object id="logout-img" class="icons" type="image/svg+xml" data="/img/profile/icons/logout.svg" title=""></object>
|
||||
<span>logout</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="main-column" slot="main">
|
||||
{#key $loaded}
|
||||
{#if $loaded==1}
|
||||
<profile-general bind:this={general} style="display: none;"></profile-general>
|
||||
<profile-groups bind:this={groups} style="display: none;"></profile-groups>
|
||||
<profile-communes bind:this={communes} style="display: none;"></profile-communes>
|
||||
<profile-coops bind:this={coops} style="display: none;"></profile-coops>
|
||||
<profile-parties bind:this={parties} style="display: none;"></profile-parties>
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
</pane-aligner>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
#general-img {
|
||||
top: 0rem;
|
||||
}
|
||||
|
||||
#groups-img {
|
||||
top: 0.3rem;
|
||||
}
|
||||
|
||||
#coops-img {
|
||||
top: 0rem;
|
||||
}
|
||||
|
||||
#parties-img {
|
||||
top: 0rem;
|
||||
}
|
||||
|
||||
#logout-img {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
#logout-button {
|
||||
padding-top: 1rem;
|
||||
padding-left: 0.1rem;
|
||||
}
|
||||
|
||||
#left-column {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 15.2rem;
|
||||
padding: 2rem;
|
||||
border-radius: 0.64rem 0.64rem 0.64rem 0.64rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.icons {
|
||||
position: relative;
|
||||
width: 1.8rem;
|
||||
}
|
||||
|
||||
#left-column button span {
|
||||
position: absolute;
|
||||
padding-left: 3.4rem;
|
||||
margin-top: 0rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
}
|
||||
|
||||
#left-column button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#main-column {
|
||||
padding: 1rem 2rem 1rem 2rem;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 0 0.64rem 0.64rem 0;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-height: 20rem;
|
||||
}
|
||||
|
||||
pane-aligner {
|
||||
--width-main: 800px;
|
||||
--width-left: 10.5rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1340px) {
|
||||
|
||||
|
||||
#left-column {
|
||||
position: relative;
|
||||
margin-left: 0rem;
|
||||
width: 100%;
|
||||
border-radius: 0.64rem 0.64rem 0rem 0;
|
||||
}
|
||||
|
||||
#main-column {
|
||||
border-radius: 0.64rem;
|
||||
padding: 3rem 0.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-radius: 0rem 0rem 0.64rem 0.64rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#logout-button {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#left-column button {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
#logout-button {
|
||||
padding-top: 1rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
29
Server/app/svelte/src/profile/profile-coops.svelte
Normal file
29
Server/app/svelte/src/profile/profile-coops.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<svelte:options tag="profile-coops" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
// Main code
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<h3>Under development</h3>
|
||||
|
||||
<p style=" position: relative; margin-top: 2rem;">Visit <a href="https://discord.gg/Qk8KUk787z" style="color: #c52a28;">https://discord.gg/Qk8KUk787z</a> and ask for your cooperative to be added.</p>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
433
Server/app/svelte/src/profile/profile-general.svelte
Normal file
433
Server/app/svelte/src/profile/profile-general.svelte
Normal file
@@ -0,0 +1,433 @@
|
||||
<svelte:options tag="profile-general" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
//Import components
|
||||
import "/js/components/select-component.js"
|
||||
import "/js/components/switch-component.js"
|
||||
|
||||
//Export statements
|
||||
|
||||
// Main code
|
||||
let emailInput
|
||||
|
||||
let section
|
||||
let saveEmailButton
|
||||
let changePasswordInputDiv
|
||||
let changePasswordMsg
|
||||
let savePasswordButton
|
||||
let passwordInput
|
||||
let changePasswordDiv
|
||||
let passwordVisibilityButton
|
||||
let emailMsg
|
||||
let passwordDiv
|
||||
let emailDiv
|
||||
let emailInputDiv
|
||||
let prevEmail
|
||||
|
||||
let context = getContext("profile-component")
|
||||
let user = context.user
|
||||
|
||||
function showSaveButton(button) {
|
||||
prevEmail = emailInput.value
|
||||
button.style.display = "initial"
|
||||
emailMsg.style.display = "inline"
|
||||
let windowWidth = window.innerWidth
|
||||
if (windowWidth<1100) {
|
||||
emailInputDiv.style.marginTop = "1rem"
|
||||
emailDiv.style.flexDirection = "column"
|
||||
}
|
||||
else {
|
||||
//emailInput.style.width = "19rem"
|
||||
}
|
||||
}
|
||||
|
||||
function saveEmail() {
|
||||
let email = emailInput.value
|
||||
if (AuthTools.checkEmail(email,emailMsg)) {
|
||||
if (email!=user.email) {
|
||||
AuthTools.changeUser("email",email,user)
|
||||
}
|
||||
resetEmailField()
|
||||
}
|
||||
}
|
||||
|
||||
function resetEmailField() {
|
||||
if (prevEmail!=undefined) {
|
||||
emailInput.value = prevEmail
|
||||
}
|
||||
emailInput.style.width = "100%"
|
||||
emailMsg.style.display = "none"
|
||||
emailDiv.style.flexDirection = "row"
|
||||
emailInputDiv.style.marginTop = "0rem"
|
||||
saveEmailButton.style.display = "none"
|
||||
emailMsg.innerHTML = ""
|
||||
}
|
||||
|
||||
function launchChangePassword() {
|
||||
let windowWidth = window.innerWidth
|
||||
if (windowWidth<1100) {
|
||||
changePasswordInputDiv.style.display = "flex";
|
||||
}
|
||||
else {
|
||||
changePasswordInputDiv.style.display = "initial";
|
||||
}
|
||||
changePasswordDiv.style.display = "none";
|
||||
passwordInput.focus()
|
||||
}
|
||||
|
||||
function savePassword() {
|
||||
let password = passwordInput.value
|
||||
if (AuthTools.checkPassword(password,changePasswordMsg)) {
|
||||
if (password!=user.password) {
|
||||
AuthTools.changeUser("password",password,user)
|
||||
}
|
||||
changePasswordMsg.innerHTML = ""
|
||||
resetPasswordField()
|
||||
}
|
||||
}
|
||||
|
||||
function resetPasswordField() {
|
||||
changePasswordInputDiv.style.display = "none";
|
||||
changePasswordDiv.style.display = "initial";
|
||||
changePasswordMsg.innerHTML = ""
|
||||
}
|
||||
|
||||
function fillFields() {
|
||||
if (user!=null && Object.keys(user).length!=0 && section!=undefined) {
|
||||
emailInput.value = user.email
|
||||
}
|
||||
else {
|
||||
setTimeout(fillFields, 10)
|
||||
}
|
||||
}
|
||||
|
||||
function resizeInput(el) {
|
||||
el.nextElementSibling.innerHTML = el.value
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
fillFields()
|
||||
|
||||
document.addEventListener("click", function(event) {
|
||||
if (passwordDiv.focused) {
|
||||
resetEmailField()
|
||||
}
|
||||
else if (emailDiv.focused) {
|
||||
resetPasswordField()
|
||||
}
|
||||
else {
|
||||
resetEmailField()
|
||||
resetPasswordField()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<section bind:this={section} id="general-section">
|
||||
<h2 class="title-highlight">General</h2>
|
||||
<div bind:this={emailDiv} on:mouseenter={emailDiv.focused=true} on:mouseleave={emailDiv.focused=false}>
|
||||
<div class="title-msg">
|
||||
<span>Email:</span>
|
||||
<span bind:this={emailMsg} id="signup-email-msg"></span>
|
||||
</div>
|
||||
<div bind:this={emailInputDiv} id="emailInputDiv">
|
||||
<button bind:this={saveEmailButton} id="save-email" class="save-button" on:click={saveEmail}>save</button>
|
||||
<div class="input-wrapper">
|
||||
<input bind:this={emailInput} id="emailInput" class="text-input" type="text" on:click={() => showSaveButton(saveEmailButton)} on:input={() => resizeInput(emailInput)}>
|
||||
<div class="ghost-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div bind:this={passwordDiv} on:mouseenter={passwordDiv.focused=true} on:mouseleave={passwordDiv.focused=false} id="change-password-line-wrapper">
|
||||
<div id="change-password-line">
|
||||
<div class="title-msg">
|
||||
<span>Password:</span>
|
||||
<span bind:this={changePasswordMsg} id="signup-password-msg"></span>
|
||||
</div>
|
||||
|
||||
<div bind:this={changePasswordDiv} id="change-password-div">
|
||||
<button id="change-password" on:click={launchChangePassword}>change
|
||||
<object type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon"></object>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div bind:this={changePasswordInputDiv} id="change-password-input-div">
|
||||
<button bind:this={savePasswordButton} id="save-password" class="save-button" on:click={savePassword}>save</button>
|
||||
<input bind:this={passwordInput} id="passwordInput" class="text-input" type="password">
|
||||
<button bind:this={passwordVisibilityButton} class="eye-icon" on:click="{() => AuthTools.changePasswordVisibility(passwordVisibilityButton)}">
|
||||
<object type="image/svg+xml" data="/img/auth/eye_icon.svg" title="eye icon"></object>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div id="verifiedDiv">
|
||||
<span>Verified:</span>
|
||||
<span style="color: {user.verified ? "green" : "red"}">{user.verified}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
#verifiedDiv {
|
||||
display: inline;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 2rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*---General section-----------------------------------------------------------*/
|
||||
|
||||
.ghost-input {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 10rem);
|
||||
min-width: 0rem;
|
||||
height: 2.5rem;
|
||||
position: relative;
|
||||
right: 0
|
||||
}
|
||||
|
||||
span {
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
#general-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#general-section h2 {
|
||||
margin: auto;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#general-section >div {
|
||||
height: 3.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-bottom: 0.14rem solid;
|
||||
border-color: #cdcdcd;
|
||||
}
|
||||
|
||||
#general-section >div >:first-child {
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
}
|
||||
|
||||
/* add padding to every line to center the diving line*/
|
||||
#general-section >div:last-child {
|
||||
padding-bottom: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
#general-section >div div,
|
||||
#general-section >div input,
|
||||
#general-section >div :not(:first-child) input {
|
||||
font-weight: 500;
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
color: #292222;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#general-section >div>:last-child {
|
||||
padding-right: 1.35rem;
|
||||
}
|
||||
|
||||
.text-input {
|
||||
position: relative;
|
||||
width: 20.475rem;
|
||||
direction: rtl;
|
||||
border: 0;
|
||||
outline: none;
|
||||
bottom: 0.341rem;
|
||||
}
|
||||
|
||||
|
||||
/*---Email-------------------------------------------------------------------*/
|
||||
|
||||
#emailInput {
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0.1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#save-email {
|
||||
display: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
#signup-email-msg,
|
||||
#signup-password-msg {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
color:red;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#signup-email-msg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#general-section >div:nth-child(2) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#emailInputDiv {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
height: 2rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title-msg {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.title-msg * {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/*---Change password-------------------------------------------------------------------*/
|
||||
|
||||
#change-password-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#change-password-div {
|
||||
width: 9.3rem;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#change-password {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
width: 8rem;
|
||||
height: 2.73rem;
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
padding-right: 2rem;
|
||||
margin-top: -0.55rem;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#change-password > object {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 1.5rem;
|
||||
right: 0.0rem;
|
||||
}
|
||||
|
||||
#change-password-input-div {
|
||||
display: none;
|
||||
float: right;
|
||||
position: relative;
|
||||
margin-top: -1.7rem;
|
||||
}
|
||||
|
||||
#passwordInput {
|
||||
width: 15rem;
|
||||
right: 0.65rem;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
position: relative;
|
||||
bottom: 0.34rem;
|
||||
margin-right: 0.6rem;
|
||||
height: 2.73rem;
|
||||
width: 4.778rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.15rem;
|
||||
color: white;
|
||||
background-color: var(--red);
|
||||
border-color: var(--red);
|
||||
border-radius: 0.512rem;
|
||||
}
|
||||
|
||||
#save-password {
|
||||
bottom: 0.6rem
|
||||
}
|
||||
|
||||
.eye-icon {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
opacity: 0.25;
|
||||
height: 2.2rem;
|
||||
width: 1.7rem;
|
||||
}
|
||||
|
||||
.eye-icon * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 1100px) {
|
||||
|
||||
#change-password-line-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
min-height: 4rem;
|
||||
}
|
||||
|
||||
#change-password-input-div {
|
||||
margin-top: 1rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#general-section >div:nth-child(2) {
|
||||
height: auto;
|
||||
min-height: 4rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#passwordInput {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#emailInput {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#save-password {
|
||||
bottom: 0rem
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
455
Server/app/svelte/src/profile/profile-groups.svelte
Normal file
455
Server/app/svelte/src/profile/profile-groups.svelte
Normal file
@@ -0,0 +1,455 @@
|
||||
<svelte:options tag="profile-groups" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { getData, sendData } from "/js/libraries/serverTools.js"
|
||||
|
||||
//Import components
|
||||
import "/js/components/select-component.js"
|
||||
import "/js/components/switch-component.js"
|
||||
|
||||
//Export statements
|
||||
|
||||
// Main code
|
||||
let section
|
||||
let userGroups = []
|
||||
let groupsRequests = []
|
||||
let content = writable({})
|
||||
let loaded = writable(0)
|
||||
let keyRequests = 0
|
||||
let numLoaded = 2
|
||||
let mainPane
|
||||
let groupsAdd
|
||||
|
||||
let membersInput
|
||||
let saveMembersButton
|
||||
let membersInputDiv
|
||||
|
||||
let contactInput
|
||||
let saveContactButton
|
||||
let contactInputDiv
|
||||
|
||||
let locale = "en"
|
||||
|
||||
let inputLocation
|
||||
let inputContact
|
||||
let inputMembers
|
||||
let pencilMembers
|
||||
let pencilContact
|
||||
let pencilButtonMembers
|
||||
let pencilButtonContact
|
||||
|
||||
let myGroupLocation
|
||||
let myGroupStatus
|
||||
|
||||
let context = getContext("profile-component")
|
||||
let maps = context.maps
|
||||
|
||||
function groups_callback(response) {
|
||||
userGroups = JSON.parse(response)
|
||||
context["userGroups"] = userGroups
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/xx/get-user-groups",groups_callback)
|
||||
|
||||
function requests_callback(response) {
|
||||
let parsed = JSON.parse(response)
|
||||
groupsRequests.push(...parsed)
|
||||
loaded.update((val) => {
|
||||
return val + 1
|
||||
})
|
||||
}
|
||||
getData("/xx/get-group-requests",requests_callback)
|
||||
|
||||
function getAddress(g) {
|
||||
if (g!=undefined) {
|
||||
let location = [g.country,g.state,g.town].filter(x => x!=null)
|
||||
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
|
||||
}
|
||||
else {
|
||||
return "Create or join group"
|
||||
}
|
||||
}
|
||||
|
||||
function getContact(c) {
|
||||
if (c==null) {
|
||||
return "https://discord.gg/Qk8KUk787z"
|
||||
}
|
||||
else {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
function launchChangeLocation() {
|
||||
showLocationOverlay()
|
||||
}
|
||||
|
||||
function launchChangeMembers() {
|
||||
|
||||
}
|
||||
|
||||
function showSaveButton(button,input) {
|
||||
if (!input.readOnly) {
|
||||
button.style.display = "initial"
|
||||
}
|
||||
}
|
||||
|
||||
function resetMembersField() {
|
||||
saveMembersButton.style.display = "none"
|
||||
}
|
||||
|
||||
function resetContactField() {
|
||||
saveContactButton.style.display = "none"
|
||||
}
|
||||
|
||||
function saveMembers() {
|
||||
let email = emailInput.value
|
||||
if (AuthTools.checkEmail(email,emailMsg)) {
|
||||
if (email!=user.email) {
|
||||
AuthTools.changeUser("email",email,user)
|
||||
}
|
||||
resetMembersField()
|
||||
}
|
||||
}
|
||||
|
||||
function saveContact() {}
|
||||
|
||||
function updateUserGroup(newInfo) {
|
||||
if (newInfo!=undefined) {
|
||||
myGroupLocation.innerHTML = getAddress(newInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function onLoadedGroups() {
|
||||
let els = [saveMembersButton,saveContactButton,membersInputDiv,contactInputDiv]
|
||||
if ($loaded==numLoaded && els.every(x => x!=undefined && x!=null)) {
|
||||
document.addEventListener("click", function(event) {
|
||||
let activeEl
|
||||
let shadowRoot = this.activeElement.shadowRoot
|
||||
if (shadowRoot!=null) {
|
||||
activeEl = shadowRoot.activeElement
|
||||
shadowRoot = activeEl.shadowRoot
|
||||
if (shadowRoot!=null) {
|
||||
activeEl = shadowRoot.activeElement
|
||||
}
|
||||
}
|
||||
if (activeEl == membersInput || activeEl == saveMembersButton) {
|
||||
resetContactField()
|
||||
}
|
||||
else if (activeEl == contactInput || activeEl == saveContactButton) {
|
||||
resetMembersField()
|
||||
}
|
||||
else {
|
||||
resetMembersField()
|
||||
resetContactField()
|
||||
}
|
||||
})
|
||||
context["updateUserGroup"] = updateUserGroup
|
||||
inputLocation = getAddress(userGroups[0])
|
||||
if (userGroups.length==0) {
|
||||
inputContact = ""
|
||||
inputMembers = ""
|
||||
}
|
||||
else {
|
||||
let group = userGroups[0]
|
||||
|
||||
inputContact = getContact(group.contact)
|
||||
inputMembers = group.members
|
||||
let status = group.status
|
||||
if (status!=undefined) {
|
||||
if (status==0) {
|
||||
myGroupStatus.innerHTML = "(pending)"
|
||||
myGroupStatus.style.color = "#FFC90E"
|
||||
}
|
||||
else if (status==2) {
|
||||
myGroupStatus.innerHTML = "(rejected)"
|
||||
myGroupStatus.style.color = "#c52a28"
|
||||
}
|
||||
pencilMembers.style.display = "none"
|
||||
pencilContact.style.display = "none"
|
||||
pencilButtonContact.style.cursor = "default"
|
||||
pencilButtonMembers.style.cursor = "default"
|
||||
membersInput.readOnly = true
|
||||
contactInput.readOnly = true
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
let f = () => onLoadedGroups()
|
||||
setTimeout(f, 100)
|
||||
}
|
||||
}
|
||||
|
||||
function focus(el) {
|
||||
el.focus()
|
||||
el.click()
|
||||
}
|
||||
|
||||
function approveRequest(ind,user_id) {
|
||||
sendData("/xx/group-approve-request",{user_id: user_id})
|
||||
groupsRequests.splice(ind,1)
|
||||
keyRequests = keyRequests + 1
|
||||
}
|
||||
|
||||
function rejectRequest(ind,user_id) {
|
||||
sendData("/xx/group-reject-request",{user_id: user_id})
|
||||
groupsRequests.splice(ind,1)
|
||||
keyRequests = keyRequests + 1
|
||||
}
|
||||
|
||||
function launchGroupsAdd() {
|
||||
groupsAdd.style.display = "block"
|
||||
mainPane.style.display = "none"
|
||||
if (maps["groupsAdd"]!=undefined) {
|
||||
maps["groupsAdd"].invalidateSize()
|
||||
}
|
||||
}
|
||||
|
||||
function closeGroupsAdd() {
|
||||
groupsAdd.style.display = "none"
|
||||
mainPane.style.display = "block"
|
||||
}
|
||||
|
||||
context["onLoadedGroups"] = onLoadedGroups
|
||||
context["launchGroupsAdd"] = launchGroupsAdd
|
||||
context["closeGroupsAdd"] = closeGroupsAdd
|
||||
|
||||
onMount(() => {
|
||||
onLoadedGroups()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key $loaded}
|
||||
{#if $loaded==numLoaded}
|
||||
<div bind:this={mainPane}>
|
||||
<h2>Groups</h2>
|
||||
<div>
|
||||
<h3 class="group-heading">My group</h3>
|
||||
<span bind:this={myGroupStatus} class="status"></span>
|
||||
</div>
|
||||
<section bind:this={section} class="entries-section">
|
||||
<div>
|
||||
<div class="change-field-line">
|
||||
<span>Location:</span>
|
||||
<div class="change-field-div">
|
||||
<button class="change-field-button" bind:this={myGroupLocation} on:click={launchGroupsAdd}>{inputLocation}
|
||||
<object type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon" class="pencil"></object>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="change-field-line">
|
||||
<span>Members:</span>
|
||||
<div bind:this={membersInputDiv} class="change-field-div input-pencil">
|
||||
<div class="save-button-wrapper">
|
||||
<button bind:this={saveMembersButton} on:click={saveMembers} class="save-button" style="display: none">save</button>
|
||||
</div>
|
||||
<input bind:this={membersInput} id="membersInput" class="text-input" type="text" bind:value={inputMembers} on:click={() => showSaveButton(saveMembersButton,membersInput)}>
|
||||
<button bind:this={pencilButtonMembers} class="text-input-pencil-button" on:click={() => {focus(membersInput)}}>
|
||||
<object bind:this={pencilMembers} type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon" class="pencil"></object>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="change-field-line">
|
||||
<span>Contact:</span>
|
||||
<div bind:this={contactInputDiv} class="change-field-div input-pencil">
|
||||
<div class="save-button-wrapper">
|
||||
<button bind:this={saveContactButton} on:click={saveContact} class="save-button" style="display: none">save</button>
|
||||
</div>
|
||||
<input bind:this={contactInput} id="contactInput" class="text-input" type="text" bind:value={inputContact} on:click={() => showSaveButton(saveContactButton,contactInput)}>
|
||||
<button bind:this={pencilButtonContact} class="text-input-pencil-button" on:click={focus(contactInput)}>
|
||||
<object bind:this={pencilContact} type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon" class="pencil"></object>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<h3>Requests</h3>
|
||||
<section bind:this={section} class="entries-section">
|
||||
{#key keyRequests}
|
||||
{#each groupsRequests as req,ind}
|
||||
<div>
|
||||
<div class="change-field-line">
|
||||
<span>{req.email}</span>
|
||||
<div class="request-button-wrapper">
|
||||
<button on:click={() => approveRequest(ind,req.user_id)} class="approve-button">approve</button>
|
||||
<button on:click={() => rejectRequest(ind,req.user_id)} class="approve-button" style="display:visible">reject</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/key}
|
||||
</section>
|
||||
</div>
|
||||
<!--Helper panes-->
|
||||
<groups-add-component bind:this={groupsAdd} style="display: none;"></groups-add-component>
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
.request-button-wrapper {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.approve-button {
|
||||
height: 2.7rem;
|
||||
padding: 0rem 1rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.15rem;
|
||||
color: white;
|
||||
background-color: var(--red);
|
||||
border-color: var(--red);
|
||||
border-radius: 0.5rem;
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.group-heading {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-block;
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: var(--sans-serif,sans-serif)
|
||||
}
|
||||
|
||||
.text-input-pencil-button {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 2.7rem;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.text-input-pencil-button object {
|
||||
top: 0rem;
|
||||
}
|
||||
|
||||
.pencil {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 1.5rem;
|
||||
right: 0.0rem;
|
||||
}
|
||||
|
||||
.change-field-div input.text-input {
|
||||
position: relative;
|
||||
width: 20.475rem;
|
||||
direction: rtl;
|
||||
border: 0;
|
||||
outline: none;
|
||||
height: 2.7rem;
|
||||
font-style: var(--sans-serif,sans-serif);
|
||||
background: transparent;
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
#membersInput {
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
#contactInput {
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -0.4rem;
|
||||
margin-right: 0.6rem;
|
||||
height: 2.7rem;
|
||||
width: 5rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.15rem;
|
||||
color: white;
|
||||
background-color: var(--red);
|
||||
border-color: var(--red);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.save-button-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.entries-section {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.entries-section >div {
|
||||
height: 3.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-bottom: 0.14rem solid;
|
||||
border-color: #cdcdcd;
|
||||
}
|
||||
|
||||
/* add padding to every line to center the diving line*/
|
||||
.entries-section >div:last-child {
|
||||
padding-bottom: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
/*---Change field-------------------------------------------------------------------*/
|
||||
|
||||
.change-field-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.change-field-div {
|
||||
width: max-content;
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.change-field-button {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 2.7rem;
|
||||
font-size: 1.15rem;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
padding-right: 1.9rem;
|
||||
margin-top: -0.55rem;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/*---General section-----------------------------------------------------------*/
|
||||
|
||||
h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
span {
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
29
Server/app/svelte/src/profile/profile-parties.svelte
Normal file
29
Server/app/svelte/src/profile/profile-parties.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<svelte:options tag="profile-parties" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
import * as AuthTools from "/js/libraries/authTools.js"
|
||||
|
||||
// Main code
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<h3>Under development</h3>
|
||||
|
||||
<p style=" position: relative; margin-top: 2rem;">Visit <a href="https://discord.gg/Qk8KUk787z" style="color: #c52a28;">https://discord.gg/Qk8KUk787z</a> and ask for your party to be added.</p>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
@@ -12,8 +12,8 @@ server {
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
location ~* \.(?:css|js|json)$ {
|
||||
expires 1d;
|
||||
location ~* \.(?:css|js|json|txt)$ {
|
||||
expires 1h;
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
@@ -22,6 +22,7 @@ server {
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
location /js/ {}
|
||||
location /css {}
|
||||
location /img/ {}
|
||||
@@ -29,16 +30,29 @@ server {
|
||||
location /fonts/ {}
|
||||
location /favicon.ico {}
|
||||
location /robots.txt {}
|
||||
location /sitemap.txt {}
|
||||
location ~ /loaderio-58f125137ee61345d68285d88016ce2a {}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8001/;
|
||||
map $request_uri $language_redirect {
|
||||
~^/(check-login|login-post|logout|signup-post|signup-google|change-user|get-user|confirm-email) ""; # Excluded URIs, no redirection
|
||||
~^/([a-zA-Z]{2})/ ""; # Matches URIs that start with a two-letter language code and sets it to an empty string
|
||||
default /en; # Redirects all other URIs to the /en prefix
|
||||
}
|
||||
|
||||
location / {
|
||||
rewrite ^ $language_redirect$request_uri? permanent;
|
||||
proxy_pass http://127.0.0.1:8001;
|
||||
}
|
||||
|
||||
rewrite /en/communities /en/communes permanent;
|
||||
rewrite /en/coops /en/cooperatives permanent;
|
||||
rewrite /en/affiliates /en/partners permanent;
|
||||
|
||||
|
||||
listen 443 http3;
|
||||
listen 443 ssl http2;
|
||||
ssl_certificate /etc/letsencrypt/live/libsoc.org/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/libsoc.org/privkey.pem; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/libsoc.org-0001/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/libsoc.org-0001/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
@@ -49,6 +63,7 @@ server {
|
||||
|
||||
resolver 1.1.1.1 1.0.0.1 valid=300s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
@@ -57,17 +72,20 @@ server {
|
||||
server_name libsoc.org;
|
||||
|
||||
listen 443 ssl http2; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/libsoc.org/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/libsoc.org/privkey.pem; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/libsoc.org-0001/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/libsoc.org-0001/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
|
||||
return 301 https://www.libsoc.org$request_uri;
|
||||
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name www.libsoc.org libsoc.org;
|
||||
|
||||
}
|
||||
|
@@ -25,8 +25,7 @@ SearchLight.Configuration.load() |> SearchLight.connect
|
||||
#SearchLight.Migration.all_up!!(context=Server)
|
||||
#SearchLight.Migration.status()
|
||||
|
||||
cd(@__DIR__)
|
||||
p = "migrations/"
|
||||
p = "db/migrations/"
|
||||
files = readdir(p)
|
||||
files = files[map(x -> x[end-1:end].=="jl", files)]
|
||||
inds = map(x -> parse(Int64,split(x,"_")[1]), files)
|
||||
@@ -35,7 +34,7 @@ files = files[inds_sorted]
|
||||
|
||||
for f in files
|
||||
try
|
||||
m = include(joinpath(p,f))
|
||||
m = include(joinpath("migrations",f))
|
||||
m.up()
|
||||
catch
|
||||
end
|
||||
|
@@ -8,12 +8,9 @@ function up()
|
||||
primary_key()
|
||||
column(:email, :string)
|
||||
column(:password, :string, limit = 100)
|
||||
column(:name, :string)
|
||||
column(:profile_picture, :int)
|
||||
column(:country, :int)
|
||||
column(:newsletter, :bool)
|
||||
column(:notifications, :int)
|
||||
column(:confirmation_code, :string)
|
||||
column(:google_id, :string)
|
||||
column(:verified, :bool)
|
||||
]
|
||||
end
|
||||
|
||||
|
31
Server/db/migrations/2022026611846565_create_table_groups.jl
Normal file
31
Server/db/migrations/2022026611846565_create_table_groups.jl
Normal file
@@ -0,0 +1,31 @@
|
||||
module CreateTableGroups
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
include("../../lib/DatabaseSupport.jl")
|
||||
import .DatabaseSupport: add_foreign_key, add_index
|
||||
|
||||
function up()
|
||||
create_table(:groups) do
|
||||
[
|
||||
primary_key()
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:members, :int)
|
||||
column(:user_id, :int)
|
||||
]
|
||||
end
|
||||
|
||||
add_foreign_key(:groups,:user_id,:users,:id)
|
||||
add_index(:groups, :user_id)
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:groups)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,25 @@
|
||||
module CreateTableCommunes
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
function up()
|
||||
create_table(:communes) do
|
||||
[
|
||||
primary_key()
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:members, :int)
|
||||
column(:status, :string)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:communes)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,29 @@
|
||||
module CreateTableCooperatives
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
function up()
|
||||
create_table(:cooperatives) do
|
||||
[
|
||||
primary_key()
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:workers, :int)
|
||||
column(:status, :string)
|
||||
column(:logo, :string)
|
||||
column(:name, :string)
|
||||
column(:market, :string)
|
||||
column(:website, :string)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:cooperatives)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,26 @@
|
||||
module CreateTableParties
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
function up()
|
||||
create_table(:parties) do
|
||||
[
|
||||
primary_key()
|
||||
column(:country, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:logo, :string)
|
||||
column(:name, :string)
|
||||
column(:website, :string)
|
||||
column(:contact, :string)
|
||||
column(:description, :string)
|
||||
column(:members, :string)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:parties)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,27 @@
|
||||
module CreateTablePartners
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
function up()
|
||||
create_table(:partners) do
|
||||
[
|
||||
primary_key()
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:logo, :string)
|
||||
column(:name, :string)
|
||||
column(:website, :string)
|
||||
column(:description, :string)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:partners)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,41 @@
|
||||
module CreateTableGroupsRequests
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
include("../../lib/DatabaseSupport.jl")
|
||||
using .DatabaseSupport
|
||||
import .DatabaseSupport: add_foreign_key, add_index, set_default
|
||||
|
||||
function up()
|
||||
create_table(:groups_requests) do
|
||||
[
|
||||
primary_key()
|
||||
column(:group_id, :integer)
|
||||
column(:user_id, :integer)
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:members,:integer)
|
||||
column(:added, :bool)
|
||||
column(:status,:Integer)
|
||||
]
|
||||
end
|
||||
|
||||
add_foreign_key(:groups_requests,:user_id,:users,:id)
|
||||
add_foreign_key(:groups_requests,:group_id,:groups,:id)
|
||||
|
||||
add_index(:groups_requests, :user_id)
|
||||
|
||||
set_default("groups_requests","added",false)
|
||||
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:groups_requests)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,34 @@
|
||||
module CreateTablePartiesRequests
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
include("../../lib/DatabaseSupport.jl")
|
||||
using .DatabaseSupport
|
||||
import .DatabaseSupport: set_default
|
||||
|
||||
function up()
|
||||
create_table(:parties_requests) do
|
||||
[
|
||||
primary_key()
|
||||
column(:id_given, :integer)
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:status, :string)
|
||||
column(:verified, :bool)
|
||||
column(:added, :bool)
|
||||
]
|
||||
end
|
||||
|
||||
set_default("parties_requests","verified",false)
|
||||
set_default("parties_requests","added",false)
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:parties_requests)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,32 @@
|
||||
module CreateTableGroupsRequests
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
include("../../lib/DatabaseSupport.jl")
|
||||
using .DatabaseSupport
|
||||
|
||||
function up()
|
||||
create_table(:groups_requests) do
|
||||
[
|
||||
primary_key()
|
||||
column(:id_given, :integer)
|
||||
column(:country, :string)
|
||||
column(:state, :string)
|
||||
column(:town, :string)
|
||||
column(:contact, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:verified, :bool)
|
||||
column(:added, :bool)
|
||||
]
|
||||
end
|
||||
|
||||
set_default("groups_requests","verified",false)
|
||||
set_default("groups_requests","added",false)
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:groups_requests)
|
||||
end
|
||||
|
||||
end
|
@@ -0,0 +1,32 @@
|
||||
module CreateTablePartiesRequests
|
||||
|
||||
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
|
||||
|
||||
include("../../lib/DatabaseSupport.jl")
|
||||
using .DatabaseSupport
|
||||
import .DatabaseSupport: set_default
|
||||
|
||||
function up()
|
||||
create_table(:parties_requests) do
|
||||
[
|
||||
primary_key()
|
||||
column(:id_given, :integer)
|
||||
column(:name, :string)
|
||||
column(:country, :string)
|
||||
column(:link, :string)
|
||||
column(:latitude, :float)
|
||||
column(:longitude, :float)
|
||||
column(:verified, :bool)
|
||||
column(:added, :bool)
|
||||
]
|
||||
end
|
||||
|
||||
set_default("parties_requests","verified",false)
|
||||
set_default("parties_requests","added",false)
|
||||
end
|
||||
|
||||
function down()
|
||||
drop_table(:parties_requests)
|
||||
end
|
||||
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user