Compare commits
125 commits
www_eleves
...
master
Author | SHA1 | Date | |
---|---|---|---|
ed6fafda45 | |||
a648941bac | |||
f523cb1e4a | |||
5c5e0c356b | |||
f631751da4 | |||
505074dfc3 | |||
9eaa1f2897 | |||
|
23839b284e | ||
|
8923b8293d | ||
|
6eeeea3654 | ||
|
e91154c9db | ||
|
57e9bc2474 | ||
|
505f15372d | ||
|
49b136a43a | ||
|
3fd72124c0 | ||
f2bbd93c75 | |||
7c256a6dcc | |||
|
dbfd1de2b9 | ||
a14d334c75 | |||
|
1a151d6cf4 | ||
|
b5b9941560 | ||
c444b702b2 | |||
d5a7f39384 | |||
f086aab2bf | |||
|
85570af7af | ||
1a225d10ca | |||
|
7c5df4e790 | ||
ab00fede2e | |||
32d9b4d60c | |||
642b45f018 | |||
49d62aa749 | |||
ea468c2469 | |||
|
8d0c245e35 | ||
|
22b5ee34d5 | ||
|
4143cb84c2 | ||
4f72214717 | |||
cfd94cb02a | |||
|
d4254e19fd | ||
64534b2bf7 | |||
871b7eaaf8 | |||
7d2662d00a | |||
|
629bb480ea | ||
86da9646d8 | |||
|
05c7174f5f | ||
7ce999c365 | |||
|
985e6561b8 | ||
f9ebe70653 | |||
|
036ba938ae | ||
4af6452003 | |||
|
0e172f5569 | ||
aa697799bc | |||
58597c8076 | |||
142b841397 | |||
75e9a5c281 | |||
|
d958b817f3 | ||
06ef17e8f5 | |||
|
a5353b0c9e | ||
|
b40b7494a4 | ||
26cb575a54 | |||
3e84c21e00 | |||
|
4a1e24c1d3 | ||
3a2502acbe | |||
7eb5ee2dfd | |||
|
5c415b6a04 | ||
|
4444649136 | ||
e420cdd0b4 | |||
26ccb5dcc9 | |||
a958ba4b2a | |||
|
f9a9009914 | ||
|
6849eaed06 | ||
0ee44ac32a | |||
6aa3c6820c | |||
30259d31bd | |||
12226dcd25 | |||
f792566d06 | |||
ff1b211bbb | |||
df690a6289 | |||
43b6dbcba1 | |||
9bc5e860ea | |||
36723000b0 | |||
8e5d9bb657 | |||
b7bf1f96a7 | |||
950d690020 | |||
978d8626b7 | |||
c7cc8dcd84 | |||
8ba44c472f | |||
a837e7ef5d | |||
4a7414cb11 | |||
29bce486d2 | |||
4e63112182 | |||
117a5eb0b1 | |||
df1140649e | |||
a61d87b161 | |||
9d9a1a09d9 | |||
8279d15c04 | |||
bc634a8c35 | |||
|
06586f8757 | ||
0233ce33c2 | |||
1561f8d350 | |||
b7aa57c149 | |||
fb034e4b65 | |||
|
5c1c12b413 | ||
def6868588 | |||
|
433568751c | ||
|
788694dae5 | ||
|
2269dd540f | ||
c7605f42ba | |||
|
17aa03295c | ||
|
ef12ad4be5 | ||
679c7a0397 | |||
7edd149351 | |||
|
b9f69d951d | ||
|
b3f838f4bc | ||
|
7cf094f091 | ||
b1eee6e163 | |||
283ba5082e | |||
efec6f582d | |||
897fc362fa | |||
|
b84d071f5a | ||
|
a2405b3e6a | ||
1361078402 | |||
|
a82a7b22c5 | ||
|
44f874722f | ||
|
d5afde9089 | ||
|
f877eda899 |
25 changed files with 2041 additions and 4327 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use nix
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,5 @@
|
||||||
node_modules
|
node_modules
|
||||||
public/build
|
public/build
|
||||||
|
result
|
||||||
|
|
||||||
|
.direnv
|
||||||
|
|
214
data/calendars.json
Normal file
214
data/calendars.json
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
{
|
||||||
|
"tree": {
|
||||||
|
"Rentrée": {
|
||||||
|
"Évènements pour les masteriens": {},
|
||||||
|
"Exchange and international students": {},
|
||||||
|
"Départements": {},
|
||||||
|
"Amphis de rentrée": {},
|
||||||
|
"Divers rentrée": {},
|
||||||
|
"Associatif divers": {},
|
||||||
|
"Visites": {}
|
||||||
|
},
|
||||||
|
"Délégation Générale Numérique": {},
|
||||||
|
"COF": {
|
||||||
|
"Évènements (COF)": {},
|
||||||
|
"Rentrée du COF": {},
|
||||||
|
"Assemblées Générales (COF)": {},
|
||||||
|
"BdA": {
|
||||||
|
"Évènements (BdA)": {},
|
||||||
|
"Spectacles du tirage BdA": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Clubs COF": {
|
||||||
|
"PLS": {},
|
||||||
|
"HackENS": {},
|
||||||
|
"Club Bouffe": {},
|
||||||
|
"Écriv'ENS": {},
|
||||||
|
"BOcal": {},
|
||||||
|
"CinéClub": {},
|
||||||
|
"Ernestophone": {},
|
||||||
|
"Club Jeux": {},
|
||||||
|
"DDR": {},
|
||||||
|
"BandarrêtdurgENS": {},
|
||||||
|
"L'Hômonerie": {},
|
||||||
|
"Ulmity": {},
|
||||||
|
"Arts pla'": {},
|
||||||
|
"Club Inutile ☔": {}
|
||||||
|
},
|
||||||
|
"BDS": {
|
||||||
|
"Évènements (BDS)": {},
|
||||||
|
"Rentrée du BDS": {}
|
||||||
|
},
|
||||||
|
"Clubs BDS": {
|
||||||
|
"Valse": {},
|
||||||
|
"Créneaux encadrés": {},
|
||||||
|
"ENSelle": {}
|
||||||
|
},
|
||||||
|
"La Nuit de l'ENS": {},
|
||||||
|
"Délégation Générale": {},
|
||||||
|
"K-Fêt": {},
|
||||||
|
"Écocampus": {},
|
||||||
|
"La Psychédéliste": {},
|
||||||
|
"Ambassadeur·rice·s santé": {}
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"dgnum-nc": {
|
||||||
|
"dmo7DgdmTnsMd8zo": {
|
||||||
|
"name": "Valse"
|
||||||
|
},
|
||||||
|
"eEPk6miCzozgPb42": {
|
||||||
|
"name": "Délégation Générale Numérique",
|
||||||
|
"short_name": "DGNum",
|
||||||
|
"color": "#27327a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eleves-ens": {
|
||||||
|
"qG6jpJPfK37Nw8ZC": {
|
||||||
|
"name": "PLS"
|
||||||
|
},
|
||||||
|
"caey6jt6pTgKengA": {
|
||||||
|
"name": "BOcal",
|
||||||
|
"color": "#e6973e"
|
||||||
|
},
|
||||||
|
"LLWm8qK9iC5YGrrR": {
|
||||||
|
"name": "Délégation Générale",
|
||||||
|
"short_name": "DG"
|
||||||
|
},
|
||||||
|
"2KGkWzBJGorxzyTW": {
|
||||||
|
"name": "La Nuit de l'ENS",
|
||||||
|
"short_name": "La Nuit"
|
||||||
|
},
|
||||||
|
"w442JdS5AaQ6czrP": {
|
||||||
|
"name": "Écriv'ENS"
|
||||||
|
},
|
||||||
|
"aoazRGFcjHSe4LxG": {
|
||||||
|
"name": "K-Fêt",
|
||||||
|
"color": "#c63b52",
|
||||||
|
"default_location": "K-Fêt"
|
||||||
|
},
|
||||||
|
"gsZtZK8c9EmREofn": {
|
||||||
|
"name": "Ernestophone"
|
||||||
|
},
|
||||||
|
"dTHrXnYgsEoSTjWB": {
|
||||||
|
"name": "Évènements (COF)",
|
||||||
|
"short_name": "COF"
|
||||||
|
},
|
||||||
|
"bCgRFByHLiCCNc55": {
|
||||||
|
"name": "Assemblées Générales (COF)",
|
||||||
|
"short_name": "AG COF"
|
||||||
|
},
|
||||||
|
"r4yJZDHjwNtH8wkR": {
|
||||||
|
"name": "Évènements (BdA)"
|
||||||
|
},
|
||||||
|
"83AkowSYPnYrjSFr": {
|
||||||
|
"name": "Spectacles du tirage BdA",
|
||||||
|
"short_name": "Spectacles",
|
||||||
|
"initial": false
|
||||||
|
},
|
||||||
|
"ZtWm3MYSi388k2yk": {
|
||||||
|
"name": "DDR"
|
||||||
|
},
|
||||||
|
"T5WoHbs4FT5A945Z": {
|
||||||
|
"name": "CinéClub"
|
||||||
|
},
|
||||||
|
"6SHG6cg9d7S3qqwD": {
|
||||||
|
"name": "Club Inutile ☔",
|
||||||
|
"initial": false
|
||||||
|
},
|
||||||
|
"TFjE83ASCMK9rfRi": {
|
||||||
|
"name": "BandarrêtdurgENS",
|
||||||
|
"short_name": "Banda"
|
||||||
|
},
|
||||||
|
"kR8fMzmf4ciop9Je": {
|
||||||
|
"name": "Club Jeux",
|
||||||
|
"short_name": "Jeux",
|
||||||
|
"color": "#5f9ae0"
|
||||||
|
},
|
||||||
|
"22rQF3gjjz8LifZC": {
|
||||||
|
"name": "La Psychédéliste",
|
||||||
|
"short_name": "Psychédéliste"
|
||||||
|
},
|
||||||
|
"AfHYkm3gqQ4fRRj5": {
|
||||||
|
"name": "HackENS",
|
||||||
|
"default_location": "Cave d'hackENS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frama-agenda": {
|
||||||
|
"qZcPZdGb5YtJHrNZ": {
|
||||||
|
"name": "ENSelle"
|
||||||
|
},
|
||||||
|
"K6iGBG47WXaKWs3Q": {
|
||||||
|
"name": "Créneaux encadrés",
|
||||||
|
"initial": false
|
||||||
|
},
|
||||||
|
"TFjE83ASCMK9rfRi": {
|
||||||
|
"name": "BandarrêtdurgENS",
|
||||||
|
"short_name": "Banda"
|
||||||
|
},
|
||||||
|
"dSYCtdC6bgyWpKyt": {
|
||||||
|
"name": "Évènements (BDS)"
|
||||||
|
},
|
||||||
|
"goXLq2dQ8LgFAjkM": {
|
||||||
|
"name": "Club Bouffe"
|
||||||
|
},
|
||||||
|
"Q8w6dw4jGLBP9ftB": {
|
||||||
|
"name": "Écocampus"
|
||||||
|
},
|
||||||
|
"T5WoHbs4FT5A945Z": {
|
||||||
|
"name": "CinéClub"
|
||||||
|
},
|
||||||
|
"zmgdYw62RatzmGDt": {
|
||||||
|
"name": "Ulmity"
|
||||||
|
},
|
||||||
|
"iXGysEGxo7EsKjwG": {
|
||||||
|
"name": "Ambassadeur·rice·s santé",
|
||||||
|
"short_name": "Amba. santé",
|
||||||
|
"color": "#f5a142"
|
||||||
|
},
|
||||||
|
"AYNpoC674yAjEmRy": {
|
||||||
|
"name": "L'Hômonerie"
|
||||||
|
},
|
||||||
|
"MJf2wnQafbLc2arS": {
|
||||||
|
"name": "Arts pla'",
|
||||||
|
"color": "#ffb969"
|
||||||
|
},
|
||||||
|
"qS7WJwyBaGRQFyX3": {
|
||||||
|
"name": "Exchange and international students",
|
||||||
|
"short_name": "Exchange"
|
||||||
|
},
|
||||||
|
"fGmpGCNZrHQkNt7L": {
|
||||||
|
"name": "Départements",
|
||||||
|
"short_name": "Dpt",
|
||||||
|
"initial": false
|
||||||
|
},
|
||||||
|
"W6qjeqEzEekNieWk": {
|
||||||
|
"name": "Évènements pour les masteriens",
|
||||||
|
"short_name": "Master"
|
||||||
|
},
|
||||||
|
"oDPMTBSma2bfo6WS": {
|
||||||
|
"name": "Amphis de rentrée",
|
||||||
|
"short_name": "Amphis",
|
||||||
|
"initial": false
|
||||||
|
},
|
||||||
|
"aPXtDmXDxmLxkERg": {
|
||||||
|
"name": "Divers rentrée",
|
||||||
|
"short_name": "Rentrée"
|
||||||
|
},
|
||||||
|
"fqK3nqqPtXLyo4Y4": {
|
||||||
|
"name": "Rentrée du COF",
|
||||||
|
"short_name": "COF"
|
||||||
|
},
|
||||||
|
"YQnfcozPDoRgNSBb": {
|
||||||
|
"name": "Rentrée du BDS",
|
||||||
|
"short_name": "BDS"
|
||||||
|
},
|
||||||
|
"tfA32kgc3GM2fH2C": {
|
||||||
|
"name": "Visites"
|
||||||
|
},
|
||||||
|
"KDNA4GrFMkkJrwNd": {
|
||||||
|
"name": "Associatif divers",
|
||||||
|
"short_name": "Asso"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
data/locations.json
Normal file
65
data/locations.json
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"nameMap": {
|
||||||
|
"Amphi Jourdan": "Amphithéâtre Jourdan",
|
||||||
|
"Cour aux Ernest": "Cour aux Ernests",
|
||||||
|
"R2-21 (Jourdan)": "R2-21",
|
||||||
|
"Salle Jean Ibanes (Jourdan, R1-07)": "Salle Jean Ibanes (R1-07)",
|
||||||
|
"Salle Madeleine Rebérioux (Jourdan, R2-02)": "Salle Madeleine Rebérioux (R2-02)",
|
||||||
|
"Salle Marcel Roncayolo (Jourdan, R2-05)": "Salle Marcel Roncayolo (R2-05)",
|
||||||
|
"Cour des bibliothèques": "Cour du NIR",
|
||||||
|
"Salle Jaurès": "Amphithéâtre Jaurès",
|
||||||
|
"Jaurès": "Amphithéâtre Jaurès",
|
||||||
|
"Dussane": "Salle Dussane",
|
||||||
|
"Cour aux Ernest": "Cour aux Ernests",
|
||||||
|
"Restaurant": "Pôt",
|
||||||
|
"29 rue d'Ulm": "Bâtiment du 29",
|
||||||
|
"Gymnase": "Gymnase Jean Prévost",
|
||||||
|
"Pôt (Restaurant)": "Pôt"
|
||||||
|
},
|
||||||
|
|
||||||
|
"rooms": {
|
||||||
|
"45 rue d'Ulm": [
|
||||||
|
"Amphithéâtre Galois",
|
||||||
|
"Bibliothèque Lettres",
|
||||||
|
"Salle Histoire",
|
||||||
|
"Salle Aron",
|
||||||
|
"Salle Cavaillès",
|
||||||
|
"Salle Dussane",
|
||||||
|
"Salle des Actes",
|
||||||
|
"Salle des Résistants",
|
||||||
|
"Salle Cavaillès",
|
||||||
|
"Salle Cartan",
|
||||||
|
"Salle Noether",
|
||||||
|
"Salle Bourbaki",
|
||||||
|
"Petite salle ECLA",
|
||||||
|
"Salle Reig ECLA",
|
||||||
|
"Salle Césaire ECLA",
|
||||||
|
"Salle Beckett",
|
||||||
|
"Cour aux Ernests",
|
||||||
|
"Cour du NIR",
|
||||||
|
"Cour Pasteur",
|
||||||
|
"Pôt",
|
||||||
|
"Petit pôt",
|
||||||
|
"Canopée",
|
||||||
|
"K-Fêt",
|
||||||
|
"Cave d'hackENS",
|
||||||
|
"Gymnase Jean Prévost",
|
||||||
|
"Salle d'expression artistique (SEA)",
|
||||||
|
"Locaux des départements lettres et sciences humaines et sociales"
|
||||||
|
],
|
||||||
|
"24 rue Lhomond": ["Salle CONF IV"],
|
||||||
|
"29 rue d'Ulm": [
|
||||||
|
"Bâtiment du 29",
|
||||||
|
"Bibliothèque des sciences expérimentales",
|
||||||
|
"Amphithéâtre Jaurès"],
|
||||||
|
"Jourdan": [
|
||||||
|
"Campus de Jourdan",
|
||||||
|
"Bibliothèque de Jourdan",
|
||||||
|
"Salle Marcel Roncayolo (R2-05)",
|
||||||
|
"Salle Madeleine Rebérioux (R2-02)",
|
||||||
|
"Amphithéâtre Jourdan",
|
||||||
|
"R2-21",
|
||||||
|
"Salle Jean Ibanes"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
47
default.nix
47
default.nix
|
@ -1,9 +1,44 @@
|
||||||
{ pkgs ? import ./nix {} }:
|
|
||||||
{
|
{
|
||||||
production = pkgs.npmlock2nix.build {
|
sources ? import ./npins,
|
||||||
src = ./.;
|
pkgs ? import sources.nixpkgs { },
|
||||||
installPhase = "cp -r public/build $out";
|
}:
|
||||||
buildCommands = [ "npm run build" ];
|
|
||||||
|
let
|
||||||
|
inherit (pkgs.lib.fileset)
|
||||||
|
fileFilter
|
||||||
|
gitTrackedWith
|
||||||
|
intersection
|
||||||
|
toSource
|
||||||
|
;
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
package = pkgs.buildNpmPackage {
|
||||||
|
name = "metis";
|
||||||
|
|
||||||
|
src = toSource {
|
||||||
|
root = ./.;
|
||||||
|
fileset = intersection (gitTrackedWith { } ./.) (
|
||||||
|
fileFilter ({ name, hasExt, ... }: !(hasExt "nix") && name != "npins") ./.
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
npmDepsHash = "sha256-RbjWNVY8KlPP9ajQRnrsWhOZiiyyMGQSY39lmZnTC1I=";
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
cp -r public $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
name = "metis.dev";
|
||||||
|
|
||||||
|
packages = [ pkgs.nodejs ];
|
||||||
|
};
|
||||||
|
|
||||||
|
providers = {
|
||||||
|
eleves-ens = "cloud.eleves.ens.fr";
|
||||||
|
frama-agenda = "framagenda.org";
|
||||||
|
dgnum-nc = "cloud.dgnum.eu";
|
||||||
};
|
};
|
||||||
shell = import ./shell.nix { inherit pkgs; };
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
# nix/default.nix
|
|
||||||
{ pkgs ? import <nixpkgs> {} }:
|
|
||||||
let
|
|
||||||
sources = import ./sources.nix;
|
|
||||||
in
|
|
||||||
import sources.nixpkgs {
|
|
||||||
overlays = [
|
|
||||||
(self: super: {
|
|
||||||
npmlock2nix = pkgs.callPackage sources.npmlock2nix { };
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
{
|
|
||||||
"niv": {
|
|
||||||
"branch": "master",
|
|
||||||
"description": "Easy dependency management for Nix projects",
|
|
||||||
"homepage": "https://github.com/nmattia/niv",
|
|
||||||
"owner": "nmattia",
|
|
||||||
"repo": "niv",
|
|
||||||
"rev": "5830a4dd348d77e39a0f3c4c762ff2663b602d4c",
|
|
||||||
"sha256": "1d3lsrqvci4qz2hwjrcnd8h5vfkg8aypq3sjd4g3izbc8frwz5sm",
|
|
||||||
"type": "tarball",
|
|
||||||
"url": "https://github.com/nmattia/niv/archive/5830a4dd348d77e39a0f3c4c762ff2663b602d4c.tar.gz",
|
|
||||||
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"branch": "master",
|
|
||||||
"description": "Nix Packages collection",
|
|
||||||
"homepage": "",
|
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "21.05",
|
|
||||||
"sha256": "1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36",
|
|
||||||
"type": "tarball",
|
|
||||||
"url": "https://github.com/nixos/nixpkgs/archive/21.05.tar.gz",
|
|
||||||
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
|
||||||
},
|
|
||||||
"npmlock2nix": {
|
|
||||||
"branch": "master",
|
|
||||||
"description": null,
|
|
||||||
"homepage": null,
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "npmlock2nix",
|
|
||||||
"rev": "ff17a3c59233911f776d8d462d61d82a3e41df34",
|
|
||||||
"sha256": "0l624gkkpn1r0g48b204k0wcqm9cwy5rzd5mnxwfjhyjj1wg4nl7",
|
|
||||||
"type": "tarball",
|
|
||||||
"url": "https://github.com/nix-community/npmlock2nix/archive/ff17a3c59233911f776d8d462d61d82a3e41df34.tar.gz",
|
|
||||||
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
|
||||||
}
|
|
||||||
}
|
|
174
nix/sources.nix
174
nix/sources.nix
|
@ -1,174 +0,0 @@
|
||||||
# This file has been generated by Niv.
|
|
||||||
|
|
||||||
let
|
|
||||||
|
|
||||||
#
|
|
||||||
# The fetchers. fetch_<type> fetches specs of type <type>.
|
|
||||||
#
|
|
||||||
|
|
||||||
fetch_file = pkgs: name: spec:
|
|
||||||
let
|
|
||||||
name' = sanitizeName name + "-src";
|
|
||||||
in
|
|
||||||
if spec.builtin or true then
|
|
||||||
builtins_fetchurl { inherit (spec) url sha256; name = name'; }
|
|
||||||
else
|
|
||||||
pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
|
|
||||||
|
|
||||||
fetch_tarball = pkgs: name: spec:
|
|
||||||
let
|
|
||||||
name' = sanitizeName name + "-src";
|
|
||||||
in
|
|
||||||
if spec.builtin or true then
|
|
||||||
builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
|
|
||||||
else
|
|
||||||
pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
|
|
||||||
|
|
||||||
fetch_git = name: spec:
|
|
||||||
let
|
|
||||||
ref =
|
|
||||||
if spec ? ref then spec.ref else
|
|
||||||
if spec ? branch then "refs/heads/${spec.branch}" else
|
|
||||||
if spec ? tag then "refs/tags/${spec.tag}" else
|
|
||||||
abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
|
|
||||||
in
|
|
||||||
builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; };
|
|
||||||
|
|
||||||
fetch_local = spec: spec.path;
|
|
||||||
|
|
||||||
fetch_builtin-tarball = name: throw
|
|
||||||
''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
|
|
||||||
$ niv modify ${name} -a type=tarball -a builtin=true'';
|
|
||||||
|
|
||||||
fetch_builtin-url = name: throw
|
|
||||||
''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
|
|
||||||
$ niv modify ${name} -a type=file -a builtin=true'';
|
|
||||||
|
|
||||||
#
|
|
||||||
# Various helpers
|
|
||||||
#
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
|
|
||||||
sanitizeName = name:
|
|
||||||
(
|
|
||||||
concatMapStrings (s: if builtins.isList s then "-" else s)
|
|
||||||
(
|
|
||||||
builtins.split "[^[:alnum:]+._?=-]+"
|
|
||||||
((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
# The set of packages used when specs are fetched using non-builtins.
|
|
||||||
mkPkgs = sources: system:
|
|
||||||
let
|
|
||||||
sourcesNixpkgs =
|
|
||||||
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
|
|
||||||
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
|
|
||||||
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
|
|
||||||
in
|
|
||||||
if builtins.hasAttr "nixpkgs" sources
|
|
||||||
then sourcesNixpkgs
|
|
||||||
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
|
|
||||||
import <nixpkgs> {}
|
|
||||||
else
|
|
||||||
abort
|
|
||||||
''
|
|
||||||
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
|
|
||||||
add a package called "nixpkgs" to your sources.json.
|
|
||||||
'';
|
|
||||||
|
|
||||||
# The actual fetching function.
|
|
||||||
fetch = pkgs: name: spec:
|
|
||||||
|
|
||||||
if ! builtins.hasAttr "type" spec then
|
|
||||||
abort "ERROR: niv spec ${name} does not have a 'type' attribute"
|
|
||||||
else if spec.type == "file" then fetch_file pkgs name spec
|
|
||||||
else if spec.type == "tarball" then fetch_tarball pkgs name spec
|
|
||||||
else if spec.type == "git" then fetch_git name spec
|
|
||||||
else if spec.type == "local" then fetch_local spec
|
|
||||||
else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
|
|
||||||
else if spec.type == "builtin-url" then fetch_builtin-url name
|
|
||||||
else
|
|
||||||
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
|
|
||||||
|
|
||||||
# If the environment variable NIV_OVERRIDE_${name} is set, then use
|
|
||||||
# the path directly as opposed to the fetched source.
|
|
||||||
replace = name: drv:
|
|
||||||
let
|
|
||||||
saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
|
|
||||||
ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
|
|
||||||
in
|
|
||||||
if ersatz == "" then drv else
|
|
||||||
# this turns the string into an actual Nix path (for both absolute and
|
|
||||||
# relative paths)
|
|
||||||
if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
|
|
||||||
|
|
||||||
# Ports of functions for older nix versions
|
|
||||||
|
|
||||||
# a Nix version of mapAttrs if the built-in doesn't exist
|
|
||||||
mapAttrs = builtins.mapAttrs or (
|
|
||||||
f: set: with builtins;
|
|
||||||
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
|
|
||||||
);
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
|
|
||||||
range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
|
|
||||||
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
|
|
||||||
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
|
|
||||||
concatMapStrings = f: list: concatStrings (map f list);
|
|
||||||
concatStrings = builtins.concatStringsSep "";
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
|
|
||||||
optionalAttrs = cond: as: if cond then as else {};
|
|
||||||
|
|
||||||
# fetchTarball version that is compatible between all the versions of Nix
|
|
||||||
builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
|
|
||||||
let
|
|
||||||
inherit (builtins) lessThan nixVersion fetchTarball;
|
|
||||||
in
|
|
||||||
if lessThan nixVersion "1.12" then
|
|
||||||
fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
|
|
||||||
else
|
|
||||||
fetchTarball attrs;
|
|
||||||
|
|
||||||
# fetchurl version that is compatible between all the versions of Nix
|
|
||||||
builtins_fetchurl = { url, name ? null, sha256 }@attrs:
|
|
||||||
let
|
|
||||||
inherit (builtins) lessThan nixVersion fetchurl;
|
|
||||||
in
|
|
||||||
if lessThan nixVersion "1.12" then
|
|
||||||
fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
|
|
||||||
else
|
|
||||||
fetchurl attrs;
|
|
||||||
|
|
||||||
# Create the final "sources" from the config
|
|
||||||
mkSources = config:
|
|
||||||
mapAttrs (
|
|
||||||
name: spec:
|
|
||||||
if builtins.hasAttr "outPath" spec
|
|
||||||
then abort
|
|
||||||
"The values in sources.json should not have an 'outPath' attribute"
|
|
||||||
else
|
|
||||||
spec // { outPath = replace name (fetch config.pkgs name spec); }
|
|
||||||
) config.sources;
|
|
||||||
|
|
||||||
# The "config" used by the fetchers
|
|
||||||
mkConfig =
|
|
||||||
{ sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
|
|
||||||
, sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
|
|
||||||
, system ? builtins.currentSystem
|
|
||||||
, pkgs ? mkPkgs sources system
|
|
||||||
}: rec {
|
|
||||||
# The sources, i.e. the attribute set of spec name to spec
|
|
||||||
inherit sources;
|
|
||||||
|
|
||||||
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
|
|
||||||
inherit pkgs;
|
|
||||||
};
|
|
||||||
|
|
||||||
in
|
|
||||||
mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }
|
|
47
npins/default.nix
Normal file
47
npins/default.nix
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Generated by npins. Do not modify; will be overwritten regularly
|
||||||
|
let
|
||||||
|
data = builtins.fromJSON (builtins.readFile ./sources.json);
|
||||||
|
version = data.version;
|
||||||
|
|
||||||
|
mkSource = spec:
|
||||||
|
assert spec ? type; let
|
||||||
|
path =
|
||||||
|
if spec.type == "Git" then mkGitSource spec
|
||||||
|
else if spec.type == "GitRelease" then mkGitSource spec
|
||||||
|
else if spec.type == "PyPi" then mkPyPiSource spec
|
||||||
|
else if spec.type == "Channel" then mkChannelSource spec
|
||||||
|
else builtins.throw "Unknown source type ${spec.type}";
|
||||||
|
in
|
||||||
|
spec // { outPath = path; };
|
||||||
|
|
||||||
|
mkGitSource = { repository, revision, url ? null, hash, ... }:
|
||||||
|
assert repository ? type;
|
||||||
|
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
||||||
|
# In the latter case, there we will always be an url to the tarball
|
||||||
|
if url != null then
|
||||||
|
(builtins.fetchTarball {
|
||||||
|
inherit url;
|
||||||
|
sha256 = hash; # FIXME: check nix version & use SRI hashes
|
||||||
|
})
|
||||||
|
else assert repository.type == "Git"; builtins.fetchGit {
|
||||||
|
url = repository.url;
|
||||||
|
rev = revision;
|
||||||
|
# hash = hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkPyPiSource = { url, hash, ... }:
|
||||||
|
builtins.fetchurl {
|
||||||
|
inherit url;
|
||||||
|
sha256 = hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkChannelSource = { url, hash, ... }:
|
||||||
|
builtins.fetchTarball {
|
||||||
|
inherit url;
|
||||||
|
sha256 = hash;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
if version == 3 then
|
||||||
|
builtins.mapAttrs (_: mkSource) data.pins
|
||||||
|
else
|
||||||
|
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
|
11
npins/sources.json
Normal file
11
npins/sources.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"pins": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"type": "Channel",
|
||||||
|
"name": "nixpkgs-unstable",
|
||||||
|
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre696114.dfffb2e7a52d/nixexprs.tar.xz",
|
||||||
|
"hash": "15dj4hcx5wqcnxwfhh1gx2rpcwyip794bkmv0vpz4f01hibr7wd2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 3
|
||||||
|
}
|
5118
package-lock.json
generated
5118
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,10 @@
|
||||||
"@fullcalendar/daygrid": "^5.10.1",
|
"@fullcalendar/daygrid": "^5.10.1",
|
||||||
"@fullcalendar/timegrid": "^5.10.1",
|
"@fullcalendar/timegrid": "^5.10.1",
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||||
|
"@rollup/plugin-replace": "^4.0.0",
|
||||||
|
"@types/bootstrap": "^5.2.0",
|
||||||
"dav": "^1.8.0",
|
"dav": "^1.8.0",
|
||||||
"postcss": "^8.3.11",
|
"postcss": "^8.3.11",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
|
@ -26,6 +29,7 @@
|
||||||
"rollup-plugin-terser": "^7.0.0",
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
"svelte": "^3.0.0",
|
"svelte": "^3.0.0",
|
||||||
"svelte-fullcalendar": "^1.1.1",
|
"svelte-fullcalendar": "^1.1.1",
|
||||||
|
"svelte-loading-spinners": "^0.1.7",
|
||||||
"svelte-reactive-preprocessor": "^0.8.0",
|
"svelte-reactive-preprocessor": "^0.8.0",
|
||||||
"sveltestrap": "^5.9.0"
|
"sveltestrap": "^5.9.0"
|
||||||
},
|
},
|
||||||
|
@ -33,8 +37,10 @@
|
||||||
"@fullcalendar/adaptive": "^5.10.1",
|
"@fullcalendar/adaptive": "^5.10.1",
|
||||||
"@fullcalendar/bootstrap5": "^5.10.2",
|
"@fullcalendar/bootstrap5": "^5.10.2",
|
||||||
"@fullcalendar/list": "^5.10.1",
|
"@fullcalendar/list": "^5.10.1",
|
||||||
|
"@fullcalendar/resource-timeline": "^5.10.1",
|
||||||
"@fullcalendar/rrule": "^5.10.1",
|
"@fullcalendar/rrule": "^5.10.1",
|
||||||
"@nextcloud/cdav-library": "^1.0.0",
|
"@nextcloud/cdav-library": "^1.0.0",
|
||||||
|
"@rollup/plugin-replace": "^4.0.0",
|
||||||
"bootstrap": "^5.1.3",
|
"bootstrap": "^5.1.3",
|
||||||
"bootstrap-icons": "^1.8.1",
|
"bootstrap-icons": "^1.8.1",
|
||||||
"ical.js": "^1.5.0",
|
"ical.js": "^1.5.0",
|
||||||
|
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
135
public/favicon.svg
Normal file
135
public/favicon.svg
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 527.36377 527.36377"
|
||||||
|
version="1.1"
|
||||||
|
id="svg881"
|
||||||
|
sodipodi:docname="favicon.svg"
|
||||||
|
width="527.36377"
|
||||||
|
height="527.36377"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
inkscape:export-filename="/home/maurice/Downloads/metis-favicon.png"
|
||||||
|
inkscape:export-xdpi="5.825201"
|
||||||
|
inkscape:export-ydpi="5.825201"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs885">
|
||||||
|
<rect
|
||||||
|
x="157.84219"
|
||||||
|
y="108.54564"
|
||||||
|
width="211.10562"
|
||||||
|
height="65.459778"
|
||||||
|
id="rect14597" />
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath16">
|
||||||
|
<path
|
||||||
|
d="M 0,82 H 135 V 0 H 0 Z"
|
||||||
|
id="path18"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview883"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="1.2151427"
|
||||||
|
inkscape:cx="199.56505"
|
||||||
|
inkscape:cy="263.75504"
|
||||||
|
inkscape:window-width="1916"
|
||||||
|
inkscape:window-height="1055"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg881" />
|
||||||
|
<!--! Font Awesome Pro 6.1.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. -->
|
||||||
|
<path
|
||||||
|
d="m 135.68189,39.68189 c 0,-17.67 14.3,-32.0000005 32,-32.0000005 17.7,0 32,14.3300005 32,32.0000005 v 32 h 128 v -32 c 0,-17.67 14.3,-32.0000005 32,-32.0000005 17.7,0 32,14.3300005 32,32.0000005 v 32 h 48 c 26.5,0 48,21.489996 48,48 v 48 H 39.681885 v -48 c 0,-26.510004 21.49,-48 48,-48 h 48.000005 z m 352,432 c 0,26.5 -21.5,48 -48,48 H 87.681885 c -26.51,0 -48,-21.5 -48,-48 v -272 H 487.68189 Z"
|
||||||
|
id="path879"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;stroke:#000000;stroke-width:15.3638;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<g
|
||||||
|
id="g12"
|
||||||
|
transform="matrix(8.1181738,0,0,-8.1181738,-136.28135,813.9558)">
|
||||||
|
<g
|
||||||
|
id="g14"
|
||||||
|
clip-path="url(#clipPath16)"
|
||||||
|
style="stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.90606594,0,0,0.90606594,5.1759162,5.1181535)">
|
||||||
|
<g
|
||||||
|
id="g24"
|
||||||
|
transform="translate(48.915,39.1377)"
|
||||||
|
style="stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none" />
|
||||||
|
<g
|
||||||
|
id="g114"
|
||||||
|
transform="matrix(1.138726,0,0,1.138726,49.836843,55.407254)"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c -1.48,-0.458 -2.619,-2.003 -3.821,-2.172 -3.179,-0.444 -6.965,2.365 -9.87,0.239 -0.486,-0.357 -1.201,-1.776 -0.24,-2.432 3.148,-2.149 7.176,-1.21 10.737,-2.169 -5.417,-4.223 -11.757,-6.894 -17.515,-10.55 0.002,3.387 2.744,5.677 3.032,9.168 0.299,3.624 -1.54,6.393 -2.115,9.584 -1.345,7.464 7.013,11.613 12.85,14.124 6.897,2.967 15.394,3.251 22.293,-0.692 -1.6,-1.249 -3.583,-1.741 -5.057,-3.357 4.232,-1.272 6.09,-4.68 8.353,-9.166 C 13.486,-1.167 8.028,-1.368 1.437,0.398 1.068,0.498 0.508,0.157 0,-0.001"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path116"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g118"
|
||||||
|
transform="matrix(1.0533081,0,0,1.0533081,46.728724,57.977314)"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c 6.503,-1.201 15.339581,-3.7536631 20.604581,0.37633686 -1.034,0.594 -2.342,0.214 -3.289,1.02600004 1.183,-0.339 2.638,-0.286 3.943,-0.64600004 -1.328,3.05700004 -3.389,7.00400004 -6.98,7.90700004 C 10.505581,9.6103369 6.535,6.194 3.612,3.752 1.053,1.614 -2.884,1.998 -6.27,1.88 c 0.623,1.958 0.605,4.212 1.959,5.786 1.226,1.427 3.305,-0.265 5.013,-0.449 -1.695,-1.122 -3.6,1.522 -4.745,0.067 -1.023,-1.299 -1.11,-3.301 -1.572,-5.025 3.109,0.483 6.499,-0.064 8.828,1.989 1.16,1.023 2.181,2.211 3.494,3.266 1.455,1.169 3.387,2.175 5.018,2.569 -9.183,3.223 -19.29,2.438 -26.929,-4.006 -1.041,-0.878 -1.786,-2.02 -1.97,-3.703 -0.24,-2.207 0.623,-3.698 1.206,-5.655 3.869,4.09 10.042,4.376 15.968,3.28"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none"
|
||||||
|
id="path120"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccccccccccccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g122"
|
||||||
|
transform="translate(46.010492,54.896804)"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none">
|
||||||
|
<path
|
||||||
|
d="m 1.8278919,1.1767903 c -5.491,1.368 -12.3006309,2.2592192 -16.8369859,-1.94838701 -2.01,-2.43999999 1.236239,-7.18439019 -0.896761,-10.61139029 -0.96,-1.546 -1.61,-3.694 -2.187,-5.317 5.143,3.314 10.5119533,5.91138 15.7109533,9.3173804 -3.416,0.905 -8.6990003,-0.542 -10.7970003,2.734 -0.612,0.957 -0.513766,4.13336544 2.817794,4.52239691 3.1899999,0.477 6.1269999,-0.728 9.38699989,-0.822 0.522,-0.016 1.754,1.297 1.754,1.297"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none"
|
||||||
|
id="path124"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g126"
|
||||||
|
transform="translate(57.289806,70.531922)"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none">
|
||||||
|
<path
|
||||||
|
d="M 0.76507845,-0.24497265 C 3.5610784,-1.2089727 5.3840784,0.93702735 7.3380784,2.2830273 -2.7240356,5.9072531 -12.30118,3.8886216 -22.402066,-1.7568934 -13.88871,1.5173778 -7.0659216,2.4490273 0.76507845,-0.24497265"
|
||||||
|
style="fill:#ffb100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none"
|
||||||
|
id="path128"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g134"
|
||||||
|
transform="translate(60.55138,64.731179)"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c 0.29,-0.175 0.599,-0.685 0.5,-0.882 -0.527,-1.054 -1.978,-0.497 -1.83,0.484 0.055,0.364 0.831,0.697 1.33,0.397"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00003;stroke-miterlimit:10;stroke-dasharray:none"
|
||||||
|
id="path136"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
id="text14595"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono';text-align:center;white-space:pre;shape-inside:url(#rect14597);fill:#000000;fill-opacity:1;stroke:none"
|
||||||
|
x="38.925781"
|
||||||
|
y="0"
|
||||||
|
transform="translate(-30.329907,-19.815924)" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 7.5 KiB |
|
@ -4,13 +4,13 @@
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||||
|
|
||||||
<title>Calendrier étudiant de l'ENS</title>
|
<title>Calendrier de la DGNum</title>
|
||||||
|
|
||||||
<link rel='icon' type='image/png' href='/calendrier/favicon.png'>
|
<link rel='icon' href='/favicon.svg'>
|
||||||
<link rel='stylesheet' href='/calendrier/global.css'>
|
<link rel='stylesheet' href='/global.css'>
|
||||||
<link rel='stylesheet' href='/calendrier/build/bundle.css'>
|
<link rel='stylesheet' href='/build/bundle.css'>
|
||||||
|
|
||||||
<script defer src='/calendrier/build/bundle.js'></script>
|
<script defer src='/build/bundle.js'></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -7,6 +7,8 @@ import css from 'rollup-plugin-css-only'
|
||||||
import postcss from 'rollup-plugin-postcss'
|
import postcss from 'rollup-plugin-postcss'
|
||||||
import dev from 'rollup-plugin-dev'
|
import dev from 'rollup-plugin-dev'
|
||||||
import copy from 'rollup-plugin-copy'
|
import copy from 'rollup-plugin-copy'
|
||||||
|
import json from '@rollup/plugin-json'
|
||||||
|
import replace from '@rollup/plugin-replace'
|
||||||
|
|
||||||
const production = !process.env.ROLLUP_WATCH
|
const production = !process.env.ROLLUP_WATCH
|
||||||
|
|
||||||
|
@ -41,6 +43,9 @@ export default {
|
||||||
file: 'public/build/bundle.js'
|
file: 'public/build/bundle.js'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
replace({
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'dev')
|
||||||
|
}),
|
||||||
svelte({
|
svelte({
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
// enable run-time checks when not in production
|
// enable run-time checks when not in production
|
||||||
|
@ -48,6 +53,8 @@ export default {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
json(),
|
||||||
|
|
||||||
// Copy font files
|
// Copy font files
|
||||||
copy({
|
copy({
|
||||||
targets: [
|
targets: [
|
||||||
|
@ -97,7 +104,6 @@ export default {
|
||||||
from: '/cal/frama-agenda',
|
from: '/cal/frama-agenda',
|
||||||
to: 'https://framagenda.org/remote.php/dav/public-calendars/'
|
to: 'https://framagenda.org/remote.php/dav/public-calendars/'
|
||||||
}
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
port: 5000
|
port: 5000
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,5 +1 @@
|
||||||
{ pkgs ? import ./nix {} }:
|
(import ./. { }).devShell
|
||||||
pkgs.npmlock2nix.shell {
|
|
||||||
src = ./.;
|
|
||||||
nodejs = pkgs.nodejs-14_x;
|
|
||||||
}
|
|
||||||
|
|
136
src/App.svelte
136
src/App.svelte
|
@ -1,5 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import FullCalendar from 'svelte-fullcalendar';
|
import FullCalendar from 'svelte-fullcalendar';
|
||||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||||
|
@ -7,16 +6,23 @@
|
||||||
import rrulePlugin from '@fullcalendar/rrule';
|
import rrulePlugin from '@fullcalendar/rrule';
|
||||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||||
import listPlugin from '@fullcalendar/list';
|
import listPlugin from '@fullcalendar/list';
|
||||||
|
import resourceTimelinePlugin from '@fullcalendar/resource-timeline';
|
||||||
import frLocale from '@fullcalendar/core/locales/fr';
|
import frLocale from '@fullcalendar/core/locales/fr';
|
||||||
import EventModal from './EventModal.svelte';
|
import EventModal from './EventModal.svelte';
|
||||||
import FilterBar from './FilterBar.svelte';
|
import FilterBar from './FilterBar.svelte';
|
||||||
import { mkSource, calendarTree, initialCalendars, getSubCalendars } from './calendar';
|
import { mkSource, calendarTree, initialCalendars, getSubCalendars } from './calendar';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import Help from './Help.svelte';
|
import Help from './Help.svelte';
|
||||||
|
import Share from './Share.svelte';
|
||||||
|
import Spinner from './Spinner.svelte';
|
||||||
|
import { Icon } from 'sveltestrap';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.css';
|
import 'bootstrap/dist/css/bootstrap.css';
|
||||||
import 'bootstrap-icons/font/bootstrap-icons.css';
|
import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||||
import bootstrap5Plugin from '@fullcalendar/bootstrap5';
|
import bootstrap5Plugin from '@fullcalendar/bootstrap5';
|
||||||
|
import { Tooltip } from 'bootstrap';
|
||||||
|
|
||||||
|
import LOCATIONS from '../data/locations.json';
|
||||||
|
|
||||||
const event = writable(null);
|
const event = writable(null);
|
||||||
|
|
||||||
|
@ -30,34 +36,79 @@
|
||||||
return time.toLocaleTimeString();
|
return time.toLocaleTimeString();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const params = new URL(document.location).searchParams.getAll('c');
|
let isLoading = true;
|
||||||
|
|
||||||
|
const allowedViews = [
|
||||||
|
'resourceTimelineDay',
|
||||||
|
'dayGridMonth',
|
||||||
|
'timeGridWeek',
|
||||||
|
'timeGridDay',
|
||||||
|
'listWeek'
|
||||||
|
];
|
||||||
|
|
||||||
|
let search = new URL(document.location).searchParams;
|
||||||
|
|
||||||
|
if (search.has('b64')) {
|
||||||
|
// On est dans le cas où les paramètres sont codés en base64
|
||||||
|
search = new URLSearchParams(window.atob(search.get('b64')));
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = search.getAll('c');
|
||||||
|
const date = search.has('d') ? new Date(search.get('d')) : now;
|
||||||
|
const view = search.get('v');
|
||||||
|
|
||||||
|
const headers = mobile
|
||||||
|
? {
|
||||||
|
left: 'title',
|
||||||
|
center: 'prev,today,next',
|
||||||
|
right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
left: 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek'
|
||||||
|
};
|
||||||
|
|
||||||
let calendar;
|
let calendar;
|
||||||
|
let spinner;
|
||||||
|
|
||||||
let options = writable({
|
let options = writable({
|
||||||
initialView: mobile ? 'listWeek' : 'timeGridWeek',
|
initialView: allowedViews.includes(view)
|
||||||
|
? view
|
||||||
|
: mobile
|
||||||
|
? 'listWeek'
|
||||||
|
: 'timeGridWeek',
|
||||||
|
initialDate: date.toString() === 'Invalid Date' ? now : date,
|
||||||
plugins: [
|
plugins: [
|
||||||
timeGridPlugin,
|
timeGridPlugin,
|
||||||
dayGridPlugin,
|
dayGridPlugin,
|
||||||
rrulePlugin,
|
rrulePlugin,
|
||||||
listPlugin,
|
listPlugin,
|
||||||
|
resourceTimelinePlugin,
|
||||||
adaptivePlugin,
|
adaptivePlugin,
|
||||||
bootstrap5Plugin
|
bootstrap5Plugin
|
||||||
],
|
],
|
||||||
locale: frLocale,
|
locale: frLocale,
|
||||||
allDayContent: '',
|
allDayContent: '',
|
||||||
headerToolbar: {
|
headerToolbar: headers,
|
||||||
left: mobile ? false : 'prev,next today',
|
buttonText: { resourceTimelineDay: 'Salles' },
|
||||||
center: 'title',
|
scrollTime: '08:00:00',
|
||||||
right: mobile
|
resourceGroupField: 'building',
|
||||||
? 'prev,today,next dayGridMonth,timeGridWeek,timeGridDay,listWeek'
|
resourceAreaWidth: '27%',
|
||||||
: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
|
resources: Object.entries(LOCATIONS.rooms).flatMap(([building, rooms]) =>
|
||||||
},
|
rooms.map(room => ({
|
||||||
|
id: `${building}-${room}`,
|
||||||
|
building,
|
||||||
|
title: room
|
||||||
|
}))
|
||||||
|
),
|
||||||
|
filterResourcesWithEvents: true,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
|
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
|
||||||
nowIndicator: true,
|
nowIndicator: true,
|
||||||
now: now,
|
now: now,
|
||||||
scrollTime: scrollTo,
|
scrollTime: scrollTo,
|
||||||
|
scrollTimeReset: false,
|
||||||
eventClick: info => {
|
eventClick: info => {
|
||||||
openModal = true;
|
openModal = true;
|
||||||
event.set(info.event);
|
event.set(info.event);
|
||||||
|
@ -67,10 +118,39 @@
|
||||||
month: mobile ? 'numeric' : 'long',
|
month: mobile ? 'numeric' : 'long',
|
||||||
day: 'numeric'
|
day: 'numeric'
|
||||||
},
|
},
|
||||||
|
loading: b => {
|
||||||
|
isLoading = b;
|
||||||
|
if (spinner) {
|
||||||
|
spinner.$set({ isLoading: b });
|
||||||
|
if (b) {
|
||||||
|
setTimeout(() => {
|
||||||
|
spinner.$set({ isLoading: false });
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
eventSources: [],
|
eventSources: [],
|
||||||
themeSystem: 'bootstrap5',
|
themeSystem: 'bootstrap5',
|
||||||
nextDayThreshold: '05:00:00',
|
nextDayThreshold: '05:00:00',
|
||||||
progressiveEventRendering: true
|
progressiveEventRendering: true,
|
||||||
|
expandRows: true,
|
||||||
|
eventDidMount: info => {
|
||||||
|
const title = info.event.extendedProps.short_name;
|
||||||
|
if (title !== undefined) {
|
||||||
|
new Tooltip(info.el, {
|
||||||
|
title: title,
|
||||||
|
trigger: 'hover',
|
||||||
|
placement: 'top'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
viewDidMount: arg => {
|
||||||
|
spinner = new Spinner({
|
||||||
|
target: arg.el,
|
||||||
|
props: { isLoading: isLoading }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
viewWillUnmount: _ => spinner.$destroy()
|
||||||
});
|
});
|
||||||
|
|
||||||
const flatten = d => {
|
const flatten = d => {
|
||||||
|
@ -99,7 +179,7 @@
|
||||||
const updateEvents = debounce(calendars => {
|
const updateEvents = debounce(calendars => {
|
||||||
options.update(opts => ({
|
options.update(opts => ({
|
||||||
...opts,
|
...opts,
|
||||||
eventSources: selectedCalendars.map(mkSource).filter(x => !!x)
|
eventSources: calendars.map(mkSource).filter(x => !!x)
|
||||||
}));
|
}));
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
|
@ -109,6 +189,15 @@
|
||||||
<div class="h-100 d-flex flex-column">
|
<div class="h-100 d-flex flex-column">
|
||||||
<h1 class="mt-3 title text-center">Calendrier de la vie étudiante à l'ENS</h1>
|
<h1 class="mt-3 title text-center">Calendrier de la vie étudiante à l'ENS</h1>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="print-toggle fs-4 no-print"
|
||||||
|
title="Imprimer"
|
||||||
|
on:click={() => window.print()}
|
||||||
|
>
|
||||||
|
<Icon name="printer" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<Share {calendar} {selectedCalendars} />
|
||||||
<Help />
|
<Help />
|
||||||
|
|
||||||
<FilterBar {calendarTree} bind:selected={selectedCalendars} {initial} />
|
<FilterBar {calendarTree} bind:selected={selectedCalendars} {initial} />
|
||||||
|
@ -116,9 +205,22 @@
|
||||||
<FullCalendar bind:this={calendar} options={$options} />
|
<FullCalendar bind:this={calendar} options={$options} />
|
||||||
|
|
||||||
<EventModal event={$event} open={openModal} {toggle} />
|
<EventModal event={$event} open={openModal} {toggle} />
|
||||||
|
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
data-domain="calendrier.dgnum.eu"
|
||||||
|
src="https://analytics.dgnum.eu/js/script.js"
|
||||||
|
></script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.print-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75em;
|
||||||
|
left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.fs-7) {
|
:global(.fs-7) {
|
||||||
font-size: 0.9rem !important;
|
font-size: 0.9rem !important;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +244,10 @@
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.fc-toolbar-chunk:not(:last-child)) {
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 765px) {
|
@media (max-width: 765px) {
|
||||||
:global(.fc-header-toolbar) {
|
:global(.fc-header-toolbar) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -155,4 +261,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
:global(.no-print, .btn, .tooltip, .offcanvas, .offcanvas-backdrop, .load-spinner) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -56,7 +56,10 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{#if event.allDay}
|
{#if event.extendedProps.simAllDay}
|
||||||
|
<Icon name="calendar-range" class="text-primary" />
|
||||||
|
<span class="ms-1">{dateFormat(event.start)} ({timeFormat(event.extendedProps.realStart)}) - {dateFormat(event.end)} ({timeFormat(event.extendedProps.realEnd)})</span>
|
||||||
|
{:else if event.allDay}
|
||||||
<Icon name="calendar-range" class="text-primary" />
|
<Icon name="calendar-range" class="text-primary" />
|
||||||
<span class="ms-1">{dateFormat(event.start)} - {dateFormat(event.end)}</span>
|
<span class="ms-1">{dateFormat(event.start)} - {dateFormat(event.end)}</span>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
const toggle = () => (isOpen = !isOpen);
|
const toggle = () => (isOpen = !isOpen);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="help-toggle fs-4" on:click={toggle}>
|
<span class="help-toggle fs-4 no-print" on:click={toggle}>
|
||||||
<Icon name="question-circle" />
|
<Icon name="question-circle" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p>Calendrier commun de la vie étudiante de l'ENS</p>
|
<p>Calendrier commun de la vie étudiante de l'ENS</p>
|
||||||
<b>Comment rajouter son calendrier :</b>
|
<b>Comment rajouter son calendrier :</b>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Utiliser un NextCloud, idéalement
|
Utiliser un NextCloud, idéalement
|
||||||
|
@ -26,15 +26,18 @@
|
||||||
</li>
|
</li>
|
||||||
<li>Créer un calendrier dessus, créer un lien de partage public.</li>
|
<li>Créer un calendrier dessus, créer un lien de partage public.</li>
|
||||||
<li>
|
<li>
|
||||||
Envoyer le lien de partage public au Club Réseau<br />
|
Envoyer le lien de partage public à la DGNum<br />
|
||||||
(club-reseau [at] lists [.] ens [.] psl [.] eu) pour faire une requête d'ajout.
|
(calendrier [at] dgnum [.] eu) pour faire une requête d'ajout.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p>Code source :
|
||||||
|
<a href="https://git.dgnum.eu/DGNum/metis">https://git.dgnum.eu/DGNum/metis</a>
|
||||||
|
</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Icon name="balloon-heart" class="text-danger fs-5" />
|
<Icon name="balloon-heart" class="text-danger fs-5" />
|
||||||
<span class="fs-7">Propulsé par le Club Réseau de l'ENS</span>
|
<span class="fs-7">Propulsé par la <a href="https://dgnum.eu">Délégation Générale Numérique</a></span>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
|
145
src/Share.svelte
Normal file
145
src/Share.svelte
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
ModalFooter,
|
||||||
|
Icon,
|
||||||
|
Toast
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import { ancestors } from './calendar';
|
||||||
|
|
||||||
|
export let calendar = null;
|
||||||
|
export let selectedCalendars = [];
|
||||||
|
|
||||||
|
let share = document.URL;
|
||||||
|
|
||||||
|
let isOpen = false;
|
||||||
|
let isToastOpen = false;
|
||||||
|
let isBinary = false;
|
||||||
|
|
||||||
|
let toastText = '';
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
isOpen = !isOpen;
|
||||||
|
updateShareLink();
|
||||||
|
};
|
||||||
|
|
||||||
|
const doShare = () => {
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(share)
|
||||||
|
.then(() => (toastText = '<b>Lien de partage copié dans le presse-papier.</b>'))
|
||||||
|
.catch(() => (toastText = 'Erreur de copie automatique.'))
|
||||||
|
.finally((isToastOpen = true));
|
||||||
|
};
|
||||||
|
|
||||||
|
const filter = calendars => {
|
||||||
|
let reduced = [];
|
||||||
|
calendars.forEach(c => {
|
||||||
|
if (!ancestors[c].some(p => calendars.includes(p))) {
|
||||||
|
reduced.push(c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return reduced;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateShareLink = () => {
|
||||||
|
const loc = document.location;
|
||||||
|
const search = new URLSearchParams();
|
||||||
|
const api = calendar.getAPI();
|
||||||
|
|
||||||
|
if (calendar !== null) {
|
||||||
|
search.append('v', api.view.type);
|
||||||
|
|
||||||
|
filter(selectedCalendars).forEach(c => search.append('c', c));
|
||||||
|
|
||||||
|
search.append('d', api.getDate().toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBinary) {
|
||||||
|
const b64 = window.btoa(search.toString());
|
||||||
|
|
||||||
|
share = `${loc.origin}${loc.pathname}?b64=${b64}`;
|
||||||
|
} else {
|
||||||
|
share = `${loc.origin}${loc.pathname}?${search.toString()}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleBinary = () => {
|
||||||
|
isBinary = !isBinary;
|
||||||
|
updateShareLink();
|
||||||
|
};
|
||||||
|
|
||||||
|
$: shareDataIcon = isBinary ? 'code-square' : 'code';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="share-toast">
|
||||||
|
<Toast autohide body isOpen={isToastOpen} on:close={() => (isToastOpen = false)}>
|
||||||
|
{@html toastText}
|
||||||
|
</Toast>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="share-btn fs-4 no-print"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
title="Partager"
|
||||||
|
on:click={toggle}
|
||||||
|
>
|
||||||
|
<Icon name="share" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<Modal {isOpen} {toggle} centered scrollable>
|
||||||
|
<ModalHeader {toggle}>
|
||||||
|
<Icon name="share" class="text-success me-2" />
|
||||||
|
<b>Partage</b>
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<p>La vue actuelle du calendrier peut être partagée avec l'URL suivante :</p>
|
||||||
|
|
||||||
|
<a id="share-url">{share}</a>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<span on:click={toggleBinary} class="share-egg" title="Partager en base64">
|
||||||
|
<Icon name={shareDataIcon} class="fs-5" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<Button size="sm" color="primary" on:click={doShare}>
|
||||||
|
<Icon name="clipboard2-heart" />
|
||||||
|
<span class="ms-1">Copier</span>
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.share-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 2.5em;
|
||||||
|
right: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-egg {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#share-url {
|
||||||
|
max-width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-toast {
|
||||||
|
position: absolute;
|
||||||
|
top: 1.25em;
|
||||||
|
left: 1em;
|
||||||
|
z-index: 1100;
|
||||||
|
}
|
||||||
|
</style>
|
25
src/Spinner.svelte
Normal file
25
src/Spinner.svelte
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<script>
|
||||||
|
import { Circle2 } from 'svelte-loading-spinners';
|
||||||
|
|
||||||
|
export let isLoading = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if isLoading}
|
||||||
|
<div class="load-spinner">
|
||||||
|
<div class="position-absolute top-50 start-50">
|
||||||
|
<Circle2 colorOuter="#e658ea" colorInner="#eaac3f" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.load-spinner {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<span class="cal w-100 d-block" on:click={handleClick}>
|
<span class="cal w-100 d-block" on:click={handleClick}>
|
||||||
<Icon name={icon} class="ms-2" />
|
<Icon name={icon} class="ms-2" />
|
||||||
<span class="ms-1">{value}</span>
|
<span class="ms-1 d-inline-block cal-title">{value}</span>
|
||||||
<span class="float-end">
|
<span class="float-end">
|
||||||
{#if url}
|
{#if url}
|
||||||
<a href={url} class="cal-link" download={value} on:click|stopPropagation>
|
<a href={url} class="cal-link" download={value} on:click|stopPropagation>
|
||||||
|
@ -54,6 +54,13 @@
|
||||||
color: #aabbfe;
|
color: #aabbfe;
|
||||||
text-shadow: 2px 2px 3px #62a1fe;
|
text-shadow: 2px 2px 3px #62a1fe;
|
||||||
}
|
}
|
||||||
|
.cal-title {
|
||||||
|
max-width: 75%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.cal {
|
.cal {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
140
src/calendar.js
140
src/calendar.js
|
@ -1,3 +1,6 @@
|
||||||
|
import CALENDARS from '../data/calendars.json'
|
||||||
|
import LOCATIONS from '../data/locations.json'
|
||||||
|
|
||||||
// https://stackoverflow.com/a/35970186
|
// https://stackoverflow.com/a/35970186
|
||||||
function invertColor(hex) {
|
function invertColor(hex) {
|
||||||
if (hex.indexOf('#') === 0) {
|
if (hex.indexOf('#') === 0) {
|
||||||
|
@ -17,59 +20,19 @@ function invertColor(hex) {
|
||||||
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF'
|
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF'
|
||||||
}
|
}
|
||||||
|
|
||||||
const clouds = {
|
const parseCalendars = () => {
|
||||||
KLUB_RESEAU: 'klub-reseau',
|
let calendars = {};
|
||||||
ELEVES_ENS: 'eleves-ens',
|
|
||||||
FRAMA_AGENDA: 'frama-agenda'
|
for (const [cloud, cals] of Object.entries(CALENDARS.sources)) {
|
||||||
|
for (const [id, attrs] of Object.entries(cals)) {
|
||||||
|
calendars[id] = { cloud: cloud, ...attrs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return calendars;
|
||||||
}
|
}
|
||||||
|
|
||||||
const calendars = {
|
const calendars = parseCalendars()
|
||||||
'5WrcagPPARQ3BD87': {
|
|
||||||
cloud: clouds.KLUB_RESEAU,
|
|
||||||
name: 'Club réseau',
|
|
||||||
color: null,
|
|
||||||
default_location: "Cave d'hackENS"
|
|
||||||
},
|
|
||||||
TFEAKjAgNFQZpNjo: {
|
|
||||||
cloud: clouds.KLUB_RESEAU,
|
|
||||||
name: 'hackENS',
|
|
||||||
color: null,
|
|
||||||
default_location: "Cave d'hackENS"
|
|
||||||
},
|
|
||||||
LLWm8qK9iC5YGrrR: {
|
|
||||||
cloud: clouds.ELEVES_ENS,
|
|
||||||
name: 'Délégation Générale',
|
|
||||||
short_name: 'DG',
|
|
||||||
color: null
|
|
||||||
},
|
|
||||||
w442JdS5AaQ6czrP: {
|
|
||||||
cloud: clouds.ELEVES_ENS,
|
|
||||||
name: "Écriv'ENS",
|
|
||||||
color: null
|
|
||||||
},
|
|
||||||
fRtjDkjrZyn6fxd8: {
|
|
||||||
cloud: clouds.ELEVES_ENS,
|
|
||||||
name: 'K-Fêt',
|
|
||||||
color: '#c63b52',
|
|
||||||
default_location: 'K-Fêt'
|
|
||||||
},
|
|
||||||
gsZtZK8c9EmREofn: {
|
|
||||||
cloud: clouds.ELEVES_ENS,
|
|
||||||
name: 'Ernestophone',
|
|
||||||
color: null
|
|
||||||
},
|
|
||||||
T5WoHbs4FT5A945Z: {
|
|
||||||
cloud: clouds.FRAMA_AGENDA,
|
|
||||||
name: 'CinéClub',
|
|
||||||
color: null
|
|
||||||
},
|
|
||||||
'6SHG6cg9d7S3qqwD': {
|
|
||||||
cloud: clouds.ELEVES_ENS,
|
|
||||||
name: 'Club Inutile ☔',
|
|
||||||
color: null,
|
|
||||||
initial: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const calendarsByName = Object.fromEntries(
|
const calendarsByName = Object.fromEntries(
|
||||||
Object.entries(calendars).map(([id, { name }]) => [name, id])
|
Object.entries(calendars).map(([id, { name }]) => [name, id])
|
||||||
|
@ -81,24 +44,21 @@ export const initialCalendars = Array.from(
|
||||||
.filter(cal => cal[1])
|
.filter(cal => cal[1])
|
||||||
.map(cal => cal[0])
|
.map(cal => cal[0])
|
||||||
|
|
||||||
export const calendarTree = {
|
export const calendarTree = CALENDARS.tree
|
||||||
'Clubs COF': {
|
|
||||||
'Club réseau': {},
|
const dfs = (p, t, l) => {
|
||||||
hackENS: {},
|
for (const [c, s] of Object.entries(t)) {
|
||||||
"Écriv'ENS": {},
|
l[c] = p === null ? [] : [p, ...l[p]]
|
||||||
CinéClub: {},
|
dfs(c, s, l)
|
||||||
Ernestophone: {},
|
}
|
||||||
'Club Inutile ☔': {}
|
|
||||||
},
|
|
||||||
COF: {
|
|
||||||
BDA: {},
|
|
||||||
AG: {}
|
|
||||||
},
|
|
||||||
BDS: {},
|
|
||||||
'Délégation Générale': {},
|
|
||||||
'K-Fêt': {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ancestors = (() => {
|
||||||
|
let l = []
|
||||||
|
dfs(null, calendarTree, l)
|
||||||
|
return l
|
||||||
|
})()
|
||||||
|
|
||||||
export function getSubCalendars(name, tree = calendarTree) {
|
export function getSubCalendars(name, tree = calendarTree) {
|
||||||
let ret
|
let ret
|
||||||
for (const [cal, subTree] of Object.entries(tree)) {
|
for (const [cal, subTree] of Object.entries(tree)) {
|
||||||
|
@ -115,7 +75,7 @@ const calendarIds = Object.keys(calendars)
|
||||||
|
|
||||||
function mkCalendarUrl(id, { cloud }, extra = {}) {
|
function mkCalendarUrl(id, { cloud }, extra = {}) {
|
||||||
return (
|
return (
|
||||||
`/calendrier/cal/${cloud}/${id}/?` +
|
`/cal/${cloud}/${id}/?` +
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
...extra,
|
...extra,
|
||||||
export: true,
|
export: true,
|
||||||
|
@ -156,17 +116,47 @@ class Calendar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findLocationId(location) {
|
||||||
|
const correctedLocation = LOCATIONS.nameMap[location] || location
|
||||||
|
|
||||||
|
const result = Object.entries(LOCATIONS.rooms).find(([building, rooms]) =>
|
||||||
|
rooms.includes(correctedLocation)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result === undefined) return undefined
|
||||||
|
const [building, _] = result
|
||||||
|
return `${building}-${correctedLocation}`
|
||||||
|
}
|
||||||
|
|
||||||
function fcEventFromjCalEvent(cal) {
|
function fcEventFromjCalEvent(cal) {
|
||||||
return function (evt) {
|
return function (evt) {
|
||||||
const start = new Date(evt.dtstart)
|
const start = new Date(evt.dtstart)
|
||||||
const end = new Date(evt.dtend)
|
const end = new Date(evt.dtend)
|
||||||
|
|
||||||
|
const allDay = !evt.dtstart.endsWith('Z')
|
||||||
|
|
||||||
|
const duration = end - start // in ms
|
||||||
|
const dayMs = 24 * 3600 * 1000
|
||||||
|
|
||||||
const fcEvent = {
|
const fcEvent = {
|
||||||
title: `${cal.short_name ?? cal.name} : ${evt.summary}`,
|
title: `${cal.short_name ?? cal.name} : ${evt.summary}`,
|
||||||
start: evt.dtstart,
|
start: evt.dtstart,
|
||||||
end: evt.dtend,
|
end: evt.dtend,
|
||||||
color: cal.color,
|
color: cal.color,
|
||||||
textColor: invertColor(cal.color),
|
textColor: invertColor(cal.color),
|
||||||
duration: end - start // in ms
|
duration: duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allDay && (duration > dayMs - 1)) {
|
||||||
|
fcEvent.allDay = true
|
||||||
|
fcEvent.simAllDay = true
|
||||||
|
|
||||||
|
fcEvent.realStart = new Date(start)
|
||||||
|
fcEvent.realEnd = new Date(end)
|
||||||
|
|
||||||
|
fcEvent.start = start.setUTCHours(0, 0, 0)
|
||||||
|
fcEvent.end = end.setUTCHours(23, 59, 59)
|
||||||
|
fcEvent.duration = end - start // Update the duration
|
||||||
}
|
}
|
||||||
|
|
||||||
fcEvent.calendar = cal.name
|
fcEvent.calendar = cal.name
|
||||||
|
@ -174,6 +164,10 @@ function fcEventFromjCalEvent(cal) {
|
||||||
fcEvent.description = evt.description
|
fcEvent.description = evt.description
|
||||||
fcEvent.location = evt.location || cal.default_location
|
fcEvent.location = evt.location || cal.default_location
|
||||||
|
|
||||||
|
if (fcEvent.location) {
|
||||||
|
fcEvent.resourceId = findLocationId(fcEvent.location)
|
||||||
|
}
|
||||||
|
|
||||||
if (evt.status) {
|
if (evt.status) {
|
||||||
fcEvent.status = evt.status
|
fcEvent.status = evt.status
|
||||||
fcEvent.classNames = [`st-${evt.status.toLowerCase()}`]
|
fcEvent.classNames = [`st-${evt.status.toLowerCase()}`]
|
||||||
|
@ -196,14 +190,6 @@ function fcEventFromjCalEvent(cal) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mkEventsFromCalendar(id, cal) {
|
|
||||||
return fetchCalendar(id, cal).then(calendar => {
|
|
||||||
if (calendar[0] !== 'vcalendar') return
|
|
||||||
const cal = new Calendar(id, calendar)
|
|
||||||
return cal.events.map(fcEventFromjCalEvent(cal))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mkSource(name) {
|
export function mkSource(name) {
|
||||||
const calendarId = calendarsByName[name]
|
const calendarId = calendarsByName[name]
|
||||||
if (!calendarId) return null
|
if (!calendarId) return null
|
||||||
|
|
Loading…
Reference in a new issue