diff --git a/.meteor/versions b/.meteor/versions index 4a9dc98..1ca3900 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -57,7 +57,7 @@ random@1.2.2 rate-limit@1.1.2 react-fast-refresh@0.2.9 react-meteor-accounts@1.0.3 -react-meteor-data@3.0.2 +react-meteor-data@3.0.3 reactive-var@1.0.13 reload@1.3.2 retry@1.1.1 diff --git a/client/main.jsx b/client/main.jsx index d2e380f..a9f7af3 100644 --- a/client/main.jsx +++ b/client/main.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {StrictMode} from 'react'; import { createRoot } from 'react-dom/client'; import { Meteor } from 'meteor/meteor'; import { App } from '/imports/ui/App'; @@ -6,5 +6,7 @@ import { App } from '/imports/ui/App'; Meteor.startup(() => { const container = document.getElementById('react-target'); const root = createRoot(container); - root.render(); + root.render( + + ); }); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..bb72d11 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,12 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import pluginReact from "eslint-plugin-react"; + + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + {files: ["**/*.{js,mjs,cjs,jsx}"]}, + {languageOptions: { globals: globals.browser }}, + pluginJs.configs.recommended, + pluginReact.configs.flat.recommended, +]; \ No newline at end of file diff --git a/imports/roles.js b/imports/roles.js index 07eb4be..d24b4a2 100644 --- a/imports/roles.js +++ b/imports/roles.js @@ -13,4 +13,13 @@ export const ROLS_DE_POBLE = { }; // as const; // export type TeamRolesKeys = keyof typeof TEAM_ROLES; -// export type TeamRolesValues = (typeof TEAM_ROLES)[TeamRolesKeys]; \ No newline at end of file +// export type TeamRolesValues = (typeof TEAM_ROLES)[TeamRolesKeys]; + + + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +// De menor a major privilegi: +// +// Usuari < Voluntari de Poble < Encarregat de Tasques < Monitor de Poble < Administrador Global + diff --git a/imports/ui/App.jsx b/imports/ui/App.jsx index f8101ce..227d054 100644 --- a/imports/ui/App.jsx +++ b/imports/ui/App.jsx @@ -5,6 +5,7 @@ import { Login } from './Login'; import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data/suspense'; import { Pobles } from './Pobles'; +import { Poble } from './Poble'; import { Necessitats } from './Necessitats'; import { Tipus } from './Tipus'; import { BarraNav } from './BarraNav/BarraNav'; @@ -22,6 +23,8 @@ export const App = () => { const [esAdministrador, setEsAdministrador] = useState(false); + // const [pobleSeleccionat, setPobleSeleccionat] = useState(null); + // const u = useTracker("user", async () => await Meteor.userAsync()); @@ -45,11 +48,12 @@ export const App = () => { Carregant...}>{user ? : } } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> }; diff --git a/imports/ui/BarraNav/BarraNav.jsx b/imports/ui/BarraNav/BarraNav.jsx index 6e7dc86..ed46184 100644 --- a/imports/ui/BarraNav/BarraNav.jsx +++ b/imports/ui/BarraNav/BarraNav.jsx @@ -19,6 +19,10 @@ const SeccióUsuaris = lazy(async () => { const module = await import('/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx'); return ({ default: module.SeccióUsuaris }); }); +const SeccióCodis = lazy(async () => { + const module = await import('/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx'); + return ({ default: module.SeccióCodis }); +}); const BarraNav = ({esAdministrador, setEsAdministrador}) => { const userId = Meteor.userId(); @@ -38,6 +42,7 @@ const BarraNav = ({esAdministrador, setEsAdministrador}) => { + { esAdministrador && } ; }; diff --git a/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx b/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx index 2a73670..8092e6a 100644 --- a/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx +++ b/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx @@ -16,22 +16,27 @@ export const BotóSecció = ({titol, linkto}) => { export const SeccióPobles = () => ; export const SeccióNecessitats = () => ; export const SeccióTipus = () => ; export const SeccióUsuaris = () => ; + +export const SeccióCodis = () => ; export const PanellSeccions = ({children}) => { @@ -40,9 +45,10 @@ export const PanellSeccions = ({children}) => { border: `1px solid #cccc`, padding: `.5rem`, borderRadius: `.5rem`, - backgroundColor: `lightslategray` + backgroundColor: `lightslategray`, + flexWrap: `wrap`, + justifyContent: `space-around` }}>{children}
; }; - diff --git a/imports/ui/Codis.jsx b/imports/ui/Codis.jsx new file mode 100644 index 0000000..b22ce5f --- /dev/null +++ b/imports/ui/Codis.jsx @@ -0,0 +1,250 @@ +import React, { Suspense, useEffect, useState, useRef, lazy } from 'react'; +import { Meteor } from 'meteor/meteor';; +import { NecessitatsCollection } from '/imports/api/necessitats.js'; +import { PoblesCollection } from '../api/pobles'; +import { TipusCollection } from '../api/tipus'; +import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data/suspense'; +import { Roles } from 'meteor/roles'; +// import { useUserId } from 'meteor/react-meteor-accounts'; +import Select from 'react-select'; + +import CreatableSelect from 'react-select/creatable'; +import AsyncCreatableSelect from 'react-select/async-creatable'; +import { BarraNav } from "./BarraNav/BarraNav"; + + + + +export const Codis = () => { + + // const [permes, setPermes] = useState(false); + const [esEditor, setEsEditor] = useState(false); + const userId = Meteor.userId(); + + useEffect(() => { + (async () => { + const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]); + setEsEditor(comprovaAdmin); + })(); + }, []); + + // console.log("isAdmin: ", isAdmin) ; + + let ambitSeleccionat = null; + + useSubscribe('necessitats'); + const necessitats = useTracker("necessitats", () => NecessitatsCollection.find().fetchAsync()); + + useSubscribe('pobles'); + const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync()); + const ambits = pobles?.map(p => p?.ambitAssociat); + + // console.log("ambits: ", ambits); + + // useSubscribe('tipus'); + // const ambits = useTracker("ambits", () => Collection.find().fetchAsync()); + + // console.log("tipus: ", tipus); + // console.log("necessitats: ", necessitats); + + // console.log("tipusSeleccionat: ", tipusSeleccionat); + + + // const filterAmbit = (inputValue) => { + // return ambits.filter((i) => + // i.toLowerCase().includes(inputValue.toLowerCase()) + // ); + // }; + + const SelectorDeRol = ({ambitSeleccionat}) => { + return <> + + "Crear nou tipus..."} + // filterOption={filterAmbit} + options={ + [... + ambits + .map((v,i) => ({ + value: v, + label: v + })) + .sort((a,b) => a.label.toLowerCase() > b.label.toLowerCase()), + { + value: "GENERAL", + label: "GENERAL" + } + ] + } + // onCreateOption={(inputValue) => Meteor.callAsync('afigT', {titol: inputValue})} + isSearchable + // loadOptions={tipus.map((v,i) => ({value: v, label: v.titol}))} + onChange={ev => { + ambitSeleccionat = ev.value; + console.log("ambitSeleccionat: ", ambitSeleccionat); + + }} + /> + + {/* */} + +
+ + + +
+ + + + +

+ + + + ({value: v._id, label: v.nomPoble})) } /> + {/* */} + +
+ + + + {ambitSeleccionat && esEditor && } + + ; + }; + + + return Carregant...} > + + + +

+ + {/* ({value: v, label: v.titol})) } + loadOptions={tipus.map((v,i) => ({value: v, label: v.titol}))} + /> */} + + {/* */} + + +
    { + ambits + .sort((a,b) => a.toLowerCase() > b.toLowerCase()) + .map(amb =>
  • + {amb}{esEditor && } +
  • ) + }
+
; +}; \ No newline at end of file diff --git a/imports/ui/Login.tsx b/imports/ui/Login.tsx index 82de457..d4e8c2c 100644 --- a/imports/ui/Login.tsx +++ b/imports/ui/Login.tsx @@ -2,7 +2,8 @@ import React, { useState } from 'react'; import { Accounts } from 'meteor/accounts-base'; import { useNavigate } from 'react-router-dom'; import { Meteor } from 'meteor/meteor'; - +import { Roles } from 'meteor/roles'; +import { ROLS_GLOBALS } from '../roles'; export const Login = () => { const [isLogin, setIsLogin] = useState( { initialState: true } ); @@ -25,7 +26,7 @@ export const Login = () => { }); }; - const handleRegistration = (e) => { + const handleRegistration = async (e) => { e.preventDefault(); // console.dir(e); const username = e.target.elements.username.value; @@ -38,11 +39,16 @@ export const Login = () => { return null; } - Accounts.createUserAsync({ + const userId = await Accounts.createUserAsync({ username, email, password - }).then(() => navigate('/')) + }); + console.log("userId deL NOU USUARI: ", userId); + userId && await Roles.addUsersToRolesAsync(userId, [ROLS_GLOBALS.USUARI]); + navigate('/'); + + return userId; }; diff --git a/imports/ui/Necessitats.jsx b/imports/ui/Necessitats.jsx index ad5b45f..deae70f 100644 --- a/imports/ui/Necessitats.jsx +++ b/imports/ui/Necessitats.jsx @@ -59,11 +59,20 @@ export const Necessitats = () => { action={d => { if (d.get('selTipus')) try { + const tipusSeleccionat = tipus.find(t => t._id === d.get('selTipus')) || ""; + const pobleSeleccionat = pobles.find(p => p._id === d.get('selPoble')) || ""; Meteor.callAsync('afigNecessitat', { ...necessitatSeleccionada || [], - titol: d.get('titol'), - tipus: d.get('selTipus') || "", - poble: d.get('selPoble') + titol: d.get('taTitol'), + tipus: tipusSeleccionat, + poble: pobleSeleccionat, + descrip: d.get('taDescripcio'), + contacte: { + nom: d.get('inContacte'), + tel: d.get('inTelefon'), + email: d.get('inEMail'), + adr: d.get('inAdreça') + } }) .then(() => setNecessitatSeleccionada(null)) .catch(err => console.error(err)) @@ -76,7 +85,7 @@ export const Necessitats = () => { >
- +

@@ -91,6 +100,7 @@ export const Necessitats = () => { formatCreateLabel={(inputValue) => "Crear nou tipus..."} defaultOptions={tipus.map((v,i) => ({value: v._id, label: v.titol})).sort((a,b) => a.label.toLowerCase() > b.label.toLowerCase()) } onCreateOption={(inputValue) => Meteor.callAsync('afigTipus', {titol: inputValue})} + defaultValue={ necessitatSeleccionada ? { value: necessitatSeleccionada.tipus._id, label: necessitatSeleccionada.tipus.titol} : ""} // loadOptions={tipus.map((v,i) => ({value: v, label: v.titol}))} /> @@ -106,7 +116,9 @@ export const Necessitats = () => { - ({value: v._id, label: v.nomPoble})) } /> + ({value: v._id, label: v.nomPoble})) } + defaultValue={ necessitatSeleccionada ? { value: necessitatSeleccionada.poble._id, label: necessitatSeleccionada.poble.nomPoble} : ""} + /> {/*
+

@@ -125,10 +139,18 @@ export const Necessitats = () => { border: `1px solid #6666` }}> Contacte -
-
-
-
+
+
+
+

@@ -173,7 +195,7 @@ export const Necessitats = () => { backgroundColor: `${'lightgreen' || 'lightcoral'}` }} > - {nec.titol}{esEditor && }) + {nec.titol} {esEditor && }) } ; }; \ No newline at end of file diff --git a/imports/ui/Poble.jsx b/imports/ui/Poble.jsx new file mode 100644 index 0000000..4b06e52 --- /dev/null +++ b/imports/ui/Poble.jsx @@ -0,0 +1,228 @@ +import React, { Suspense, useEffect, useState, useRef, lazy } from 'react'; +import { Meteor } from 'meteor/meteor'; +import { PoblesCollection } from '/imports/api/pobles.js'; +import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data/suspense'; + +import { Roles } from 'meteor/roles'; +// import { useUserId } from 'meteor/react-meteor-accounts'; +import { BarraNav } from "./BarraNav/BarraNav"; +import { useParams } from "react-router-dom"; +import { NecessitatsCollection } from '../api/necessitats'; + + + + +export const Poble = () => { + + let { ambitPoble } = useParams(); + + // const [pobleSeleccionat, setPobleSeleccionat] = useState(null); + // const [creantPoble, setCreantPoble] = useState(false); + useSubscribe('pobles'); + useSubscribe('usuaris'); + useSubscribe('necessitats', ambitPoble); + // const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync()); + const pobles = useFind(PoblesCollection, [{}, {sort: {nomPoble: 1}}]); + const necessitats = useFind(NecessitatsCollection, [{}, {sort: {titol: 1}}]); + + + const [esEditor, setEsEditor] = useState(false); + const userId = Meteor.userId(); + let pob; + + // console.log("ambitPoble: ", ambitPoble); + // console.log("pobles: ", pobles); + pob = pobles.find(p => p.ambitAssociat === ambitPoble); + // console.log("pob: ", pob); + + // console.log("isAdmin: ", isAdmin) ; + + // (async () => { + useEffect(() => { + (async () => { + const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]); + setEsEditor(comprovaAdmin); + + })(); + }, []); + // })(); + + + + // const QuadreInfo_Poble = () => { + + // const refInAmbitAssignat = useRef(); + + // return
+ + // {pobleSeleccionat &&

{pobleSeleccionat._id}

} + + //
{ + // try { + // Meteor.callAsync('editaOAfigPoble', + // { + // ...pobleSeleccionat || [], + // nomPoble: d.get('nomPoble'), + // cp: d.get('cp') || "", + // comarca: d.get('comarca') || "", + // ambitAssociat: d.get('ambit-associat') + // }).then(() => setPobleSeleccionat(null)) + // .catch(err => console.error(err)); + // } catch (err) { + // alert(err); + // console.error(err); + // } + // }} + // > + // + // refInAmbitAssignat.current.value = ev.target.value.replace(/ +/g, '-').toLowerCase()} + // />
+ //
+ //
+ + // { esEditor && <> + //

+ // Àmbit associat: + + // + // {/* // nomPoble.replace(/ +/g, '-').toLowerCase()}/> */} + //

+ // } + + // + // + //
+ //
; + // }; + + return Carregant...} > + +

{pob?.nomPoble}

+ +

Monitores:

+
    + { + (async () => { + const monitors = await Roles.getUsersInRoleAsync("monitor_de_poble", pob?.ambitAssociat); + // console.log("monitors: ", monitors); + return monitors?.map( (u) =>
  • {u.username}
  • ); + })() + } +
+ +

Encarregades de tasques:

+
    + { + (async () => { + const encarregats = await Roles.getUsersInRoleAsync("encarregat_de_tasques", pob?.ambitAssociat); + // console.log("encarregats: ", encarregats); + return encarregats?.map( (u) =>
  • {u.username}
  • ); + })() + } +
+ +

Voluntàries de poble:

+
    + { + (async () => { + const voluntaris = await Roles.getUsersInRoleAsync("voluntari_de_poble", pob?.ambitAssociat); + // console.log("voluntaris: ", voluntaris); + return voluntaris?.map( (u) =>
  • {u.username}
  • ); + })() + } +
+ +

Necessitats:

+ +
    { + necessitats + .sort((a,b) => a.titol?.toLowerCase() > b.titol?.toLowerCase()) + .map(nec =>
  • + {nec.titol} {esEditor && }
  • ) + }
+ + {/* { esEditor && (pobleSeleccionat || creantPoble) && } + +
    { + pobles + .sort((a,b) => a.nomPoble?.toLowerCase() > b.nomPoble?.toLowerCase()) + .map(pob =>
  • + {pob.nomPoble}{esEditor && }
  • ) + }
+ {esEditor && } */} +
; +}; \ No newline at end of file diff --git a/imports/ui/Pobles.jsx b/imports/ui/Pobles.jsx index 3eb370c..bacdebb 100644 --- a/imports/ui/Pobles.jsx +++ b/imports/ui/Pobles.jsx @@ -6,14 +6,215 @@ import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data/susp import { Roles } from 'meteor/roles'; // import { useUserId } from 'meteor/react-meteor-accounts'; import { BarraNav } from "./BarraNav/BarraNav"; +import { Link } from 'react-router-dom'; +import { NecessitatsCollection } from '../api/necessitats'; +const QuadreInfo_Poble = ({esEditor, pobleSeleccionat, setPobleSeleccionat}) => { + + const refInAmbitAssignat = useRef(); + return
+ + {pobleSeleccionat &&

{pobleSeleccionat._id}

} + +
{ + try { + Meteor.callAsync('editaOAfigPoble', + { + ...pobleSeleccionat || [], + nomPoble: d.get('nomPoble'), + cp: d.get('cp') || "", + comarca: d.get('comarca') || "", + ambitAssociat: d.get('ambit-associat') + }).then(() => setPobleSeleccionat(null)) + .catch(err => console.error(err)); + } catch (err) { + alert(err); + console.error(err); + } + }} + > + + refInAmbitAssignat.current.value = ev.target.value.replace(/ +/g, '-').toLowerCase()} + />
+
+
+ + { esEditor && <> +

+ Àmbit associat: + + + {/* // nomPoble.replace(/ +/g, '-').toLowerCase()}/> */} +

+ } + + + +
+
; +}; + +const BotoPobleAmbIndicadors = ({pob, pobleSeleccionat, setPobleSeleccionat, esEditor}) => { + // let pob = pobleSeleccionat; + useSubscribe('necessitats', pob.ambitAssociat); + const necessitats = useFind(NecessitatsCollection, [{"poble.ambitAssociat": pob.ambitAssociat}]); + const [monitors, setMonitors] = useState(false); + const [encarregats, setEncarregats] = useState(false); + const [voluntaris, setVoluntaris] = useState(false); + + // let mon, mons, enc, encs, vol, vols; + + (async () => { + const mons = await Roles.getUsersInRoleAsync("monitor_de_poble", pob?.ambitAssociat); + // console.log("monitors: ", monitors); + setMonitors(mons?.fetch().length || false); + + const encs = await Roles.getUsersInRoleAsync("encarregat_de_tasques", pob?.ambitAssociat); + // console.log("monitors: ", monitors); + setEncarregats(encs?.fetch().length || false); + + const vols = await Roles.getUsersInRoleAsync("voluntari_de_poble", pob?.ambitAssociat); + // console.log("monitors: ", monitors); + setVoluntaris(vols?.fetch().length || false); + })(); + + return
  • + {// Indicadors de Responsables +
    + { + monitors + ? {monitors} + : false + } + { encarregats + ? {encarregats} + : false + } + { voluntaris + ? {voluntaris} + : false + } +
    + } + + {// Indicadors de Necessitats +
    + { + necessitats.length + ? + {necessitats.length } + : false + } +
    + } + + {// Botó de creació de nou poble + + {pob.nomPoble} {esEditor && } + + } +
  • ; +} export const Pobles = () => { const [pobleSeleccionat, setPobleSeleccionat] = useState(null); + const [creantPoble, setCreantPoble] = useState(false); useSubscribe('pobles'); + useSubscribe('usuaris'); + // const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync()); const pobles = useFind(PoblesCollection, [{}, {sort: {nomPoble: 1}}]); @@ -31,54 +232,20 @@ export const Pobles = () => { }, []); // })(); - const QuadreInfo_Poble = () => { + + + + + + // const Indicador = ({valor, ambit, }) => { + + // }; + + return <> - return
    - - {pobleSeleccionat &&

    {pobleSeleccionat._id}

    } -

    Pobles

    +

    Pobles

    -
    { - try { - Meteor.callAsync('editaOAfigPoble', - { - ...pobleSeleccionat || [], - nomPoble: d.get('nomPoble'), - cp: d.get('cp') || "", - comarca: d.get('comarca') || "", - }).then(() => setPobleSeleccionat(null)) - .catch(err => console.error(err)); - } catch (err) { - alert(err); - console.error(err); - } - }} - > -
    -
    -
    - - -
    -
    ; - }; - - return Carregant...} > - - { esEditor && } + { esEditor && (pobleSeleccionat || creantPoble) && }
      { }}>{ pobles .sort((a,b) => a.nomPoble?.toLowerCase() > b.nomPoble?.toLowerCase()) - .map(pob =>
    • - {pob.nomPoble}{esEditor && }
    • ) + .map(pob => { + return + }) }
    -
    ; + + {esEditor && } + ; }; \ No newline at end of file diff --git a/imports/ui/Usuaris.jsx b/imports/ui/Usuaris.jsx index d0a96cd..a1f0955 100644 --- a/imports/ui/Usuaris.jsx +++ b/imports/ui/Usuaris.jsx @@ -7,6 +7,438 @@ import { Roles } from 'meteor/roles'; // import { useUserId } from 'meteor/react-meteor-accounts'; +// import { Meteor } from 'meteor/meteor'; +// import React, {useState, useEffect, Suspense} from 'react'; +// import { useTracker, useFind } from 'meteor/react-meteor-data/suspense'; + +// import { FilesCol } from '/imports/api/files.js'; +import { useNavigate, Link } from 'react-router-dom'; + +// import { Roles } from 'meteor/roles'; +// import { useUserContext } from '/imports/api/contexts/AppContext'; +// import { groupBy } from 'lodash'; + +//import Radium from 'radium'; + + +// const IndicadorMissatges = ({notif}) => { +// return +// {notif && } +// +// ; +// }; + +const U = ({u, esAdministrador, setUsrSeleccionat}) => { + + const [adm, setAdm] = useState(false); + const [mon, setMon] = useState(false); + const [enc, setEnc] = useState(false); + const [vol, setVol] = useState(false); + + (async () => { + const esAdmin = await Roles.userIsInRoleAsync(u._id, ["admin"]); + setAdm(esAdmin); + const esMonit = await Roles.userIsInRoleAsync(u._id, ["monitor_de_poble"], {anyScope: true}); + setMon(esMonit); + const esEncar = await Roles.userIsInRoleAsync(u._id, ["encarregat_de_tasques"], {anyScope: true}); + setEnc(esEncar); + const esVolun = await Roles.userIsInRoleAsync(u._id, ["voluntari_de_poble"], {anyScope: true}); + setVol(esVolun); + // setEsAdministrador(comprovaAdmin); + })(); + + + // console.log("adm: ", adm); + // console.log("mon: ", mon); + // console.log("enc: ", enc); + // console.log("vol: ", vol); + + + // const [esAdministrador, setEsAdministrador] = useState(false); + +// const u = useTracker("user", async () => await Meteor.userAsync()); +// const userId = Meteor.userId(); + + + // useEffect(() => { + // (async () => { + // const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]); + // setEsAdministrador(comprovaAdmin); + // })(); + // }, [Meteor.userId()]); + + // const u = useUserContext(); + + + // console.log("UUU: ", u); + + const navigate = useNavigate(); + // const files = useTracker(() => { + // const filesHandle = Meteor.subscribe('files.avatar'); + // // const docsReadyYet = filesHandle.ready(); + // const files = FilesCol?.find({"meta.userId": Meteor.userId()}, {sort: {name: 1}}).fetch(); // Meteor.userId() ?? "nop" + + // return files; + // }); + +// const uname = useTracker(() => Meteor.user({ fields: { 'username': 1 } })?.username ); + +// const [mostraMenu, setMostraMenu] = useState(false); + + //const avatarLink = u.avatarLink; + + // alert("avLnk: ", u.profile.avatarLink); + + return +
    { + // setMostraMenu(true); + // }} + + // title="Logout" + + onClick={ev => { + ev.stopPropagation(); + ev.preventDefault(); + + // console.log("u: ", u); + + navigate(`/${u.username}`); + + }} + > +
    + { (u && adm) &&
    ADMIN
    } + { (u && mon) &&
    mon
    } + { (u && enc) &&
    enc
    } + { (u && vol) &&
    vol
    } +
    + + + {u?.username} + + { + ev.preventDefault(); + ev.stopPropagation(); + + // alert("Click en missatges"); + }} + > + {} + {/* */} + +
    + { + // mostraMenu && + //
    { + // // setMostraMenu(false);p + // // }} + // > + // {/* */} + //
    { + // //setEditaPerfil(true); + // ev.stopPropagation(); + // ev.preventDefault(); + + // navigate(`/c/config`); + + // }} + // >Configuración
    + // {/* */} + + //
    { + // Meteor.logout(err => { + // err && alert(`Problema: ${err}`); + // }); + // navigate(`/`); + // }} + // >Cierra la sesión
    + //
    + } +

    +
    ; +}; + + + +const AssignadorDeRols = ({pobles, esEditor, rols, usrSeleccionat, ambitsUSel}) => { + const [creantRol, setCreantRol] = useState(false); + const [ambitGeneral, setAmbitGeneral] = useState(false); + // const [ambitsUSel, setAmbitsUSel] = useState([]); + // let ambitsUSel; + // let ambitsUSel = []; + // useEffect(() => { + // (async () => { + // const ambits = await Roles.getScopesForUserAsync(usrSeleccionat?._id); + // // console.log("ambitsUSel: ", ambitsUSel); + // // ambitsUSel = ambits; + // // setAmbitsUSel(ambits); + // ambitsUSel = ambits; + // // console.log("ambitsUSel: ", ambitsUSel); + // })(); + // }, []); + + return
    +

    Rols

    + +

    Àmbits

    +
    General:
    +
      { + rols?.map(uRol =>
    • + {uRol} + {esEditor && } +
    • ) + }
    + + {/*
      */} + { + ambitsUSel.map(async aus => { + + console.log("usrSeleccionat: ", usrSeleccionat); + console.log("ambitsUSel: ", ambitsUSel); + const uRol = await Roles.getRolesForUserAsync(usrSeleccionat._id, {scope: aus, onlyScoped: true}); + return <> +
      {aus}:
      + { + {uRol} + {esEditor && } + + }
      + ; + }) + } + {/*
    */} + + {esEditor && creantRol &&
    +
    { + if (ambitGeneral) { + Meteor.callAsync('assignaRol', usrSeleccionat._id, d.get('selRol')); + } else { + Meteor.callAsync('assignaRol', usrSeleccionat._id, d.get('selRol'), d.get('selAmbit')); + } + }}> + + Àmbit:
    + + Rol:

    + + + +
    +
    } + + {esEditor && usrSeleccionat && } +
    ; +}; + +const QuadreInfo_Usuari = ({usrSeleccionat, esEditor, pobles, rols, ambitsUSel}) => { + + return
    + + {usrSeleccionat &&

    {usrSeleccionat._id}

    } + +
    { + try { + Meteor.callAsync('editaOAfigPoble', + { + ...usrSeleccionat || [], + nomPoble: d.get('nomPoble'), + cp: d.get('cp') || "", + comarca: d.get('comarca') || "", + }).then(() => setUsrSeleccionat(null)) + .catch(err => console.error(err)); + } catch (err) { + alert(err); + console.error(err); + } + }} + > +
    +
    +
    + + +
    + + { esEditor && } +
    ; +}; + + export const Usuaris = () => { const [usrSeleccionat, setUsrSeleccionat] = useState(null); @@ -18,90 +450,103 @@ export const Usuaris = () => { const usuaris = useTracker("usuaris", () => { Meteor.subscribe('usuaris'); - return Meteor.users.find().fetch().filter(u => u._id !== Meteor.userId()); + return Meteor.users.find().fetch();//.filter(u => u._id !== Meteor.userId()); }); // console.log("isAdmin: ", isAdmin) ; console.log("usuaris: ", usuaris); + + // const [ambitsUSel, setAmbitsUSel] = useState([]); // (async () => { - useEffect(() => { - (async () => { - const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]); - setEsEditor(comprovaAdmin); - })(); - - }, []); + + // let ambitsUSel = []; + + useEffect(() => { + (async () => { + const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]); + setEsEditor(comprovaAdmin); + + // console.log("ambitsUSel: ", ambitsUSel); + })(); + + }, []); + + const ambitsUSel = Roles.getScopesForUser(usrSeleccionat?._id); + // setAmbitsUSel(ambits); + + + const rols = useTracker("rols", async () => { + return await Roles.getRolesForUserAsync(usrSeleccionat?._id); + }); // })(); - const QuadreInfo_Usuari = () => { + // const [monitors, setMonitors] = useState(false); + // const [encarregats, setEncarregats] = useState(false); + // const [voluntaris, setVoluntaris] = useState(false); + // const [administradors, setAdministradors] = useState(false); + + // let mon, mons, enc, encs, vol, vols; + // let + // administradors, + // monitors, + // encarregats, + // voluntaris + // ; - return
    - - {usrSeleccionat &&

    {usrSeleccionat._id}

    } - -
    { - try { - Meteor.callAsync('editaOAfigPoble', - { - ...usrSeleccionat || [], - nomPoble: d.get('nomPoble'), - cp: d.get('cp') || "", - comarca: d.get('comarca') || "", - }).then(() => setUsrSeleccionat(null)) - .catch(err => console.error(err)); - } catch (err) { - alert(err); - console.error(err); - } - }} - > -
    -
    -
    - - -
    -
    ; - }; + + // (async () => { + // const admins = await Roles.getUsersInRoleAsync("administrador"); + // // console.log("admins: ", admins); + // administradors = await admins?.fetchAsync(); + // console.log("administradors: ", administradors); + + // const mons = await Roles.getUsersInRoleAsync("monitor_de_poble"); + // // console.log("mons: ", mons); + // monitors = await mons?.fetchAsync(); + // console.log("monitors: ", monitors); + + // const encs = await Roles.getUsersInRoleAsync("encarregat_de_tasques"); + // // console.log("encs: ", encs); + // encarregats = await encs?.fetchAsync(); + // console.log("encarregats: ", encarregats); + + // const vols = await Roles.getUsersInRoleAsync("voluntari_de_poble"); + // // console.log("vols: ", vols); + // voluntaris = await vols?.fetchAsync(); + // console.log("voluntaris: ", voluntaris); + // })(); + + + return Carregant...} >

    Usuaris

    - { esEditor && } + { esEditor && }
      { usuaris .sort((a,b) => a.username?.toLowerCase() > b.username?.toLowerCase()) .map(usr =>
    • - {usr.username}{esEditor && }
    • ) + {/* {usr.username}{esEditor && } */} + + ) }
    ; }; \ No newline at end of file diff --git a/package.json b/package.json index 279ce12..505f5b4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "meteor-node-stubs": "^1.2.5", "react": "19", "react-dom": "19", + "react-router": "^7.0.2", "react-router-dom": "^6.28.0", "react-select": "^5.8.3", "styled-components": "^6.1.13" @@ -22,5 +23,9 @@ "server": "server/main.js" }, "testModule": "tests/main.js" + }, + "devDependencies": { + "babel-plugin-react-compiler": "^19.0.0-beta-55955c9-20241229", + "eslint-plugin-react-compiler": "^19.0.0-beta-55955c9-20241229" } } diff --git a/server/main.js b/server/main.js index 1d38f67..98febb9 100644 --- a/server/main.js +++ b/server/main.js @@ -184,7 +184,16 @@ Meteor.startup(async () => { return PoblesCollection.find(); }); - Meteor.publish("necessitats", function () { + Meteor.publish("necessitats", function (ambits) { + + if (Array.isArray(ambits)) { + return; + } + + if (ambits) { + return NecessitatsCollection.find({"poble.ambitAssociat": ambits}); + } + return NecessitatsCollection.find(); }); @@ -194,13 +203,20 @@ Meteor.startup(async () => { Meteor.publish('usuaris', async function (uid) { - const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); - const userRoles = await Roles.getRolesForUserAsync(Meteor.userId()); + const esAdmin = await Roles.userIsInRoleAsync(this.userId, "admin", null); + const userRoles = await Roles.getRolesForUserAsync(this.userId); console.log("userRoles: ", userRoles); - if (uid) { - return Meteor.users.find({_id: uid},{fields: {username: 1, avatarId: 1, avatarLink: 1}}); + console.log("esAdmin: ", esAdmin); + + if (esAdmin) { + return Meteor.users.find({}); } + + if (uid) { + return Meteor.users.find({_id: uid}, {fields: {username: 1, avatarId: 1, avatarLink: 1}}); + } + return Meteor.users.find({},{fields: {username: 1, avatarId: 1, avatarLink: 1}}); }); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -213,33 +229,53 @@ Meteor.startup(async () => { await Roles.createRoleAsync(ROLS_DE_POBLE.ENCARREGAT, { unlessExists: true }); await Roles.createRoleAsync(ROLS_DE_POBLE.VOLUNTARI, { unlessExists: true }); - await Roles.addRolesToParentAsync([ROLS_DE_POBLE.VOLUNTARI, ROLS_DE_POBLE.ENCARREGAT], ROLS_DE_POBLE.MONITOR); - await Roles.addRolesToParentAsync([ROLS_DE_POBLE.VOLUNTARI, ROLS_DE_POBLE.ENCARREGAT, ROLS_DE_POBLE.MONITOR, ROLS_GLOBALS.USUARI], ROLS_GLOBALS.ADMINISTRADOR); + // await Roles.addRolesToParentAsync([ROLS_DE_POBLE.VOLUNTARI, ROLS_DE_POBLE.ENCARREGAT], ROLS_DE_POBLE.MONITOR); + // await Roles.addRolesToParentAsync([ROLS_DE_POBLE.VOLUNTARI, ROLS_DE_POBLE.ENCARREGAT, ROLS_DE_POBLE.MONITOR, ROLS_GLOBALS.USUARI], ROLS_GLOBALS.ADMINISTRADOR); } catch (err) { console.error(err.message); } // Publish user's own roles -Meteor.publish(null, function () { - if (this.userId) { - return Meteor.roleAssignment.find({ "user._id": this.userId }); - } - this.ready(); +Meteor.publish(null, async function () { + // if (await Roles.userIsInRoleAsync(this.userId, "admin")) { + return Meteor.roleAssignment.find(); + // } + + // if (this.userId) { + // return Meteor.roleAssignment.find({ "user._id": this.userId }); + // } + // this.ready(); }); // Publish roles for specific scope // Meteor.publish("scopeRoles", function (scope) { // if (this.userId) { // return Meteor.roleAssignment.find({ scope: scope }); -// } +// }2024-12-19 21:40:26 // this.ready(); // }); +Accounts.onCreateUser(async (options, user) => { + await Roles.addUsersToRolesAsync(user._id, ROLS_GLOBALS.USUARI); + return user; +}); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Meteor.methods({ +Meteor.methods({ + 'assignaRol': async function (userId, rols, ambits) { + const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); + esAdmin && await Roles.addUsersToRolesAsync(userId, rols, ambits); + console.log(`Rol "${rols}" assignat a l'usuari "${userId}" delimitat amb l'àmbit "${ambits}"`); + }, + + 'retiraRols': async function (rols, userId, ambits) { + const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); + esAdmin && await Roles.removeUsersFromRolesAsync(userId, rols, ambits); + + console.log(`Retirats els rols "${rols}" de l'usuari "${userId}"`); + }, 'editaOAfigPoble': async function (poble) { const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); @@ -305,6 +341,20 @@ Meteor.publish(null, function () { } }, + 'eliminaUsuari': async function (usuariId) { + const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); + try { + console.log(`ELIMINACIÓ D'USUARI sol·licitada per a ${usuariId}. Comprovant si ${Meteor.userId()} és Admin: `, esAdmin); + if (esAdmin && usuariId) { + return await Meteor.users.removeAsync(usuariId); + } else { + throw new Error("El nom de l'usuari no és vàlid"); + } + } catch (e) { + console.error(e); + } + }, + 'afigTipus': async function (tipus) { const esAdmin = await Roles.userIsInRoleAsync(Meteor.userId(), "admin"); try {