Encara no funciona la comprovació de rols a la barra. Tampoc està factoritzat l'estat de logueig.

This commit is contained in:
Pasq G 2024-12-06 11:48:15 +01:00
parent 04d436a10f
commit 2b4cccb598
7 changed files with 324 additions and 75 deletions

View File

@ -7,6 +7,9 @@ import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data/susp
import { Pobles } from './Pobles';
import { Necessitats } from './Necessitats';
import { Tipus } from './Tipus';
import { BarraNav } from './BarraNav/BarraNav';
import { UserStat } from './BarraNav/UserStat';
const Loguejat = lazy(async () => await import('/imports/ui/Loguejat.jsx'));
@ -17,6 +20,10 @@ export const App = () => {
// const userId = await Meteor.userAsync();
return <BrowserRouter>
<UserStat />
<BarraNav />
<Routes>
<Route path="/" element={ <Suspense fallback={<>Carregant...</>}>{user ? <Loguejat /> : <Login/>}</Suspense> } />
<Route path="/pobles" element={ <Pobles /> } />

View File

@ -0,0 +1,70 @@
import React, {useState, useEffect} from "react";
import { Link } from 'react-router-dom';
import { Roles } from 'meteor/roles';
const BotóSecció = ({titol, linkto}) => {
return <div style={{
border: `1px solid #aaaa`,
borderRadius: `.3rem`,
padding: `.2rem`,
backgroundColor: `lightblue`,
margin: `.5rem`
}}>
<Link to={linkto} >{titol}</Link>
</div>;
};
const SeccióPobles = () => <BotóSecció
titol="Pobles"
linkto="/pobles"
/>;
const SeccióNecessitats = () => <BotóSecció
titol="Necessitats"
linkto="/necessitats"
/>;
const SeccióTipus = () => <BotóSecció
titol="Tipus"
linkto="/tipus"
/>;
const SeccióUsuaris = () => <BotóSecció
titol="Usuaris"
linkto="/usuaris"
/>;
const PanellSeccions = ({children}) => {
return <><div style={{
display: `flex`,
border: `1px solid #cccc`,
padding: `.5rem`,
borderRadius: `.5rem`,
backgroundColor: `lightslategray`
}}>{children}</div>
<br />
</>;
};
const BarraNav = () => {
const [esAdministrador, setEsAdministrador] = useState(false);
const userId = Meteor.userId();
useEffect(() => {
(async () => {
const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]);
setEsAdministrador(comprovaAdmin);
})();
}, []);
return <PanellSeccions >
{ esAdministrador && <SeccióUsuaris />}
<SeccióPobles />
<SeccióNecessitats />
<SeccióTipus />
</PanellSeccions>;
};
export { BarraNav };

View File

@ -0,0 +1,191 @@
import { Meteor } from 'meteor/meteor';
import React, {useState, useEffect} from 'react';
import { useTracker } 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 <span style={{
position: `relative`
}}>
{notif && <span className='fas fa-solid fa-envelope' />}
<span style={{
borderRadius: `50%`,
background: notif ? `red` : ``,
position: `absolute`,
width: `.4em`,
aspectRatio: `1 / 1`
}}/>
</span>;
};
const UserStat = ({setEditaPerfil}) => {
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);
})();
}, []);
// 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 <>
<div
style={{
// color: `lightblue`,
top: `1em`,
// right: `1em`,
left: `1em`,
cursor: `pointer`,
background: `yellow`,
border: `lightblue 3px solid`,
//padding: `.15em`,
borderRadius: `.7em`,
fontWeight: `bold`,
display: `inline-block`
}}
onMouseEnter={ev => {
setMostraMenu(true);
}}
// title="Logout"
onClick={ev => {
ev.stopPropagation();
ev.preventDefault();
// console.log("u: ", u);
navigate(`/${u.username}`);
}}
>
{ esAdministrador && <div style={{
color: `red`,
fontSize: `xx-small`,
// textAlign: `right`
}}>ADMIN</div> }
<img
style={{
width: `3em`,
height: `3em`,
borderRadius: `50%`,
overflow: `hidden`,
verticalAlign: `middle`,
margin: `.3em`,
border: `solid 4px whitesmoke`
}}
src={u?.profile?.avatarLink}
/>
<span style={{
verticalAlign: `middle`,
margin: `0.3em 0.3em 0.3em auto`
}}>{u?.username}</span>
<span
style={{
verticalAlign: `middle`,
margin: `0.3em 0.3em 0.3em auto`,
padding: `.2em`,
// border: `2px solid grey`,
color: `grey`,
// borderRadius: `.3em`
}}
onClick={ev => {
ev.preventDefault();
ev.stopPropagation();
alert("Click en missatges");
}}
>
<IndicadorMissatges notif={false} />
</span>
</div>
{
mostraMenu &&
<div style={{
position: `absolute`,
background: `white`,
// right: `0`,
left: 0,
top: `5em`,
padding: `.4em .5em`,
border: `1px #aaa`,
cursor: `pointer`,
zIndex: `200`
}}
onMouseLeave={ev => {
setMostraMenu(false);
}}
>
{/* <Link to="/c/config"> */}
<div className="menuOp" style={{
padding: `.2em`
}}
onClick={ev => {
//setEditaPerfil(true);
ev.stopPropagation();
ev.preventDefault();
navigate(`/c/config`);
}}
>Configuración</div>
{/* </Link> */}
<div className="menuOp" style={{
padding: `.2em`
}}
onClick={() => {
Meteor.logout(err => {
err && alert(`Problema: ${err}`);
});
navigate(`/`);
}}
>Cierra la sesión</div>
</div>
}
<br /><br />
</>;
};
export { UserStat };

View File

@ -2,47 +2,10 @@ import React from "react";
import { useUserId } from 'meteor/react-meteor-accounts';
import { Link } from 'react-router-dom';
// import { styled } from 'styled-components'
// import { BarraNav } from "./BarraNav/BarraNav";
// if (loguejat)
const BotóSecció = ({titol, linkto}) => {
return <div style={{
border: `1px solid #aaaa`,
borderRadius: `.3rem`,
padding: `.2rem`,
backgroundColor: `lightblue`,
margin: `.5rem`
}}>
<Link to={linkto} >{titol}</Link>
</div>;
};
const SeccióPobles = () => <BotóSecció
titol="Pobles"
linkto="/pobles"
/>;
const SeccióNecessitats = () => <BotóSecció
titol="Necessitats"
linkto="/necessitats"
/>;
const SeccióTipus = () => <BotóSecció
titol="Tipus"
linkto="/tipus"
/>;
const PanellSeccions = ({children}) => {
return <div style={{
display: `flex`,
border: `1px solid #cccc`,
padding: `.5rem`,
borderRadius: `.5rem`,
backgroundColor: `lightslategray`
}}>{children}</div>;
};
const Loguejat = () => {
const userId = useUserId();
@ -52,12 +15,7 @@ const Loguejat = () => {
<br /><br />
<PanellSeccions >
<SeccióPobles />
<SeccióNecessitats />
<SeccióTipus />
</PanellSeccions>
{/* <BarraNav /> */}
<p>- poble (que cada poble només tinga accés a lo del seu poble, la resta de pobles no els han de poder tocar): Alaquàs / Albal / Aldaia / Alfafar / Algemesí / Alginet / Alcàsser / Benetússer / Beniparrell / Carlet / Catarroja / Dosaigües / Godelleta / Guadassuar / Iàtova / L'Alcúdia / Llocnou de la Corona / Massanassa / Paiporta / Parke Alkosa / Picanya / Sedaví / Setaigües / Utiel / València-Castellar Oliveral / València-Forn d'Alcedo / València-La Torre / Xest / Xiva / Llombai / Tremolar </p>
<p>- estat: impossible de trobar / disponible / pendent / enviada / ha arribat (que cada poble puga tocar aquestes opcions dels subministres per al seu poble)</p>

View File

@ -10,6 +10,7 @@ import { Roles } from 'meteor/roles';
import CreatableSelect from 'react-select/creatable';
import AsyncCreatableSelect from 'react-select/async-creatable';
import { BarraNav } from "./BarraNav/BarraNav";
@ -50,10 +51,8 @@ export const Necessitats = () => {
borderRadius: `.3em`,
backgroundColor: `lightcyan`
}}>
{ esEditor && <div style={{
color: `red`
}}>ADMINISTRADOR</div> }
{necessitatSeleccionada && <h1>{necessitatSeleccionada._id}</h1>}
{necessitatSeleccionada && <h2>{necessitatSeleccionada._id}</h2>}
<h1>Necessitats</h1>
<form

View File

@ -5,6 +5,7 @@ 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";
@ -38,27 +39,25 @@ export const Pobles = () => {
borderRadius: `.3em`,
backgroundColor: `lightcyan`
}}>
{ esEditor && <div style={{
color: `red`
}}>ADMINISTRADOR</div> }
{pobleSeleccionat && <h1>{pobleSeleccionat._id}</h1>}
{pobleSeleccionat && <h2>{pobleSeleccionat._id}</h2>}
<h1>Pobles</h1>
<form
action={d => {
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);
}
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);
}
}}
>
<label htmlFor="nomPoble">Població: </label><input name="nomPoble" type="text" defaultValue={ pobleSeleccionat ? pobleSeleccionat.nomPoble : ""}/><br />

View File

@ -10,6 +10,7 @@ import Select from 'react-select';
import CreatableSelect from 'react-select/creatable';
import AsyncCreatableSelect from 'react-select/async-creatable';
import { BarraNav } from "./BarraNav/BarraNav";
@ -40,10 +41,17 @@ export const Tipus = () => {
useSubscribe('tipus');
const tipus = useTracker("tipus", () => TipusCollection.find().fetchAsync());
console.log("tipus: ", tipus);
console.log("necessitats: ", necessitats);
// console.log("tipus: ", tipus);
// console.log("necessitats: ", necessitats);
console.log("tipusSeleccionat: ", tipusSeleccionat);
// console.log("tipusSeleccionat: ", tipusSeleccionat);
const filterTipus = (inputValue) => {
return tipus.filter((i) =>
i.titol.toLowerCase().includes(inputValue.toLowerCase())
);
};
const QuadreInfo_Tipus = () => {
@ -54,9 +62,7 @@ export const Tipus = () => {
borderRadius: `.3em`,
backgroundColor: `lightcyan`
}}>
{ esEditor && <div style={{
color: `red`
}}>ADMINISTRADOR</div> }
{tipusSeleccionat && <h1>{tipusSeleccionat._id}</h1>}
<h1>Tipus</h1>
@ -87,11 +93,13 @@ export const Tipus = () => {
si no
tansols podrà assignar a un tipus predefinit o al de ALTRES
*/}
<AsyncCreatableSelect
name="selTipus"
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})}
isSearchable
// loadOptions={tipus.map((v,i) => ({value: v, label: v.titol}))}
/>
@ -159,10 +167,27 @@ export const Tipus = () => {
{/* <SelectSearch options={options} value="sv" name="language" placeholder="Choose your language" /> */}
{/* <ul>{
<ul style={{
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-evenly`,
rowGap: `.3em`,
alignContent: `space-around`
}}>{
tipus
.sort((a,b) => a.nomPoble?.toLowerCase() > b.nomPoble?.toLowerCase())
.map(pob => <li key={`pob_${pob._id}`}>{pob.nomPoble}{esEditor && <button onClick={() => {setPobleSeleccionat(pob)}}>Edita</button>}</li>)
}</ul> */}
.sort((a,b) => a.titol?.toLowerCase() > b.titol?.toLowerCase())
.map(titol => <li
key={`titol_${titol._id}`}
style={{
display: `block`,
border: `1px solid #6666`,
borderRadius: `.4em`,
padding: `4px`,
listStyle: `none`,
backgroundColor: `${'lightgreen' || 'lightcoral'}`
}}
>
{titol.titol}{esEditor && <button onClick={() => {setTipusSeleccionat(titol)}}>Edita</button>}</li>)
}</ul>
</Suspense>;
};