No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Reto: roles complejos

17/30
Recursos

Aportes 10

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Saludos, compa帽eros! (0,0)/

Aqu铆 les comparto mi progreso del curso y los retos aplicados. Si alguien le sirve de referencia o si alguien gusta proporcionar algo de feedback, ya sea en buenas practicas o sugerencias, se agradece y mucho.

Repo.
GitHub page.

Advierto que s贸lo trabaj茅 funcionalidad, por lo que no esperen un trabajo est茅tico xD.
Los usuarios admin del sistema son: Irisval, RetaxMaster, freddier, alex. Los usuarios 鈥渘ormales鈥 son juandc, nameless.

Hardcoded data.

  • Para manejar los datos 鈥渉ardcodeados鈥 opt茅 por crear archivos para la informaci贸n del usuario (user.js), blogs (blogdata.js) y los roles de usuario (roles.js). Todos ubicados en src/data/.

  • Inspirandome en aportaciones dadas por compa帽eros en clases anteriores, remov铆 la lista de Admins y agregu茅 nuevas propiedades como phonenumber, description y roles, este 煤ltimo siendo un objeto el cual guarda un arreglo de roles.

  • Para el manejo de roles implement茅 el uso de constantes. En este caso s贸lo cre茅 dos roles: ADMIN (admin) y USER (user).

  • Agregu茅 a los blogs una propeidad comments el cual, obviamente, guarda un arreglo de comentarios.

  • La estructura de un comentario es la de un objeto con las propiedades content y author.

Services.

  • Las funcionalidades de Auth del archivo auth.js visto en el curso las trabaj茅 como un servicio. Por lo que cre茅 la carpeta src/services/.

  • Por comodidad cre茅 un servicio user.js. Esto con el fin de manipular la data del usuario y desarrollar funciones acorde a este. Buscando mantener auth.js lo m谩s posible a s贸lo cosas relacionadas a funciones de autentificaci贸n.

  • Cre茅 un servicio que funciona como la BD de usuarios. usersDB.js. Esto con el fin de servir de apoyo para las funciones de actualizaci贸n de usuarios.

  • Para la manipulaci贸n de los blogs opt茅 por crear un servicio al igual que auth: blog.js. Siendo este una copia del auth.js ya que utilizo la mismas funciones o caracter铆sticas. Un provider, un useBlog y BlogRoute (ojo en este 煤ltimo).

  • BlogRoute. Durante el proceso me di cuenta que si acced铆a a un blog que no existe la app truena. Por lo que redirecciono al usuario en caso de que no exista el blog que indica en la url.

  • BlogProvider/BlogContext. Adem谩s de gestionar los blogs, tambi茅n sus comentarios. Dej茅 todos los m茅todos dentro de este servicio, aunque otra opci贸n ser铆a crear un servicio de comentarios para manipularlos desde ah铆 utilizando useBlog. Algo parecido a useUser.

Gesti贸n de blogs.

  • Habilit茅 las funciones de eliminar blogs. Permitiendo dicha funci贸n unicamente al propietario del blog o los usuarios admin.

  • A帽ad铆 un formulario para la creaci贸n de nuevos blogs (s贸lo para usuarios con una sesi贸n iniciada).

  • A帽ad铆 una secci贸n de comentarior en el detalle de un blog y la funci贸n de agregar un nuevo comentario (s贸lo para usuarios con una sesi贸n iniciada).

  • Al igual que los blogs, los usuarios pueden borrar sus comentarios. Pero un usuario admin puede cualquier comentario.

UX login.

  • En caso de no tener una sesi贸n iniciada, la vista blog cuenta con un bot贸n para dirigir al usuario a la vista de login. Una vez iniciada la sesi贸n, el usuario volver谩 a la vista de blog.

  • La funcionalidad anterior tambi茅n fue implementada desde la secci贸n de comentarios de un blog.

Editar perfil de usuario.

  • Adem谩s de agregar m谩s datos para el perfil de los usuarios, tambi茅n agregu茅 la posibilidad de cambiar estos datos.

  • 脷nicamente permito actualizar el n煤mero de tel茅fono y la descripci贸n del usuario.

  • username no es un campo v谩lido a modificar (al menos en este proyecto) debido a que funciona como el identificador del usuario y los blogs y comentarios los relaciono a partir de este.

  • Es posible ver el perfil de otros usuarios ingresando su username en la ruta: e.j. profile/juandc.

  • Los usuarios admin pueden modificar el perfil de otros usuarios.

Posibles mejoras.

  • Agregar estilos (por supuesto xD).

  • Implementar funci贸n de Editar (blogs y comentarios).

  • Guardar comentarios con alg煤n identificador y/o datetime para facilitar la gesti贸n de estos.

  • El propietario del blog sea capaz de eliminar u ocultar comentarios de sus blogs.

  • Uso de alg煤n ID para los usuarios (number, hash, etc.).

  • Editar roles de usuario.

  • Ajustes en los servicios.

Lo logr茅!

Despu茅s de muchos intentos y muchas pruebas, pude hacerlo sin buscar la respuesta en internet.
.
Lo primero que hice fue cambiar la forma en la que se logeaban los usuarios. Agregue la clase User para que todos tengan la misma estructura.

auth.js

class User {
    constructor(username) {
        this.username = username
        this.roles = {}
    }
}

export function AuthProvider({ children }) {
    const navigate = useNavigate()
    const [user, setUser] = useState(null)

    const login = ({ username, locationAfterLogin }) => {
        const existingUser = users.find(user => user.username === username)

        if (existingUser) setUser(existingUser)
        else {
            const newUser = new User(username)
            users.push(newUser)
            setUser(newUser)
        }

        navigate(locationAfterLogin)
    }

    const logout = () => {
        setUser(null)
    }

    const auth = { user, users, login, logout }
    return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>
}

Esa fue la parte f谩cil. Lo segundo fue usar useParams() en ProfilePage. Cambie la ruta de 鈥/profile鈥 a 鈥/profile/:username鈥, luego hice comprobaciones para saber si se trataba de la cuenta del usuario logeado o no.

ProfilePage.js

export function ProfilePage() {
    const { user, users } = useAuth()
    const { username } = useParams()
    let isAuthor

    const userProfile = users.find(user => user.username === username)
    if (!userProfile) return <p>sorry! {username} isn't a logged user.</p>

    if (userProfile === user) isAuthor = true

    if (isAuthor)
        return (
            <>
                <h1>Hello, {username}</h1>
                <button>Edit your profile here!</button>
            </>
        )
    return <h1>Profile of {username}</h1>
}

Surgi贸 el problema que m谩s me cost贸 resolver: el link en Menu redirigia a 鈥/profile鈥, pero esa ruta ya no exist铆a. Prob茅 muchas cosas.

  1. Cambiar la ruta a 鈥/profile/${user.username}鈥, pero el menu no se re-renderizaba al cambiar el user, resultando en que siempre lleve al primer usuario logeado.
  2. Agregar un Navigate a ProfilePage si username no exist铆a, pero claro, la ruta 鈥/profile鈥 en ning煤n momento llevaba a mi componente.
  3. Usar Outlet (ni yo sab铆a como) o renderProps (no me acuerdo por qu茅 pens茅 que eso tendr铆a sentido)
  4. Agregar Navigate to=鈥/profile/{user.username}鈥/> en App, pero App no tiene acceso a user.
    Finalmente, agregu茅 un componente ProfileRedirect en ProfilePage.js

ProfilePage.js

export function ProfileRedirect() {
    const { user } = useAuth()
    if (user) return <Navigate to={`/profile/${user.username}`} />
    return <NotFound />
}

App.js

<Route
                            path="/profile/:username"
                            element={<ProfilePage />}
                        />
                        <Route path="/login" element={<LoginPage />} />

Siento que no quer铆a llegar a esta soluci贸n, no me parece la m谩s elegante, pero es la que consegu铆 despu茅s de muchos intentos. Y funciona perfecto!
.
Por 煤ltimo, cualquier sugerencia va a ser agradecida. Gracias por leer 馃槃

Hola amigos, les comparto el deploy de mi app y el repositorio por si a alguien le sirve

Github Pages - Deploy

Repositorio

La lista de admins son [ JuanDC - Freddier - Retaxmaster ]

La aplicaci贸n tiene persistencia en local storage de los blogs y usuarios, puedes crear tus propios blogs, puedes eliminar tus blogs pero si inicias sesi贸n con otro user ya no podr谩s hacerlo, a no ser que sean un administrador, un admin puede hacer todo

No lo logr茅. Comparto una forma de no hacerlo. Yo cre茅 un array aparte para simular un 鈥渂ackend鈥 y cre茅 las rutas de la misma forma que en Blogpage y BlogPost

//Este es el "backend"
const publicData = [];
publicData.push({
    name: 'Jhonny',
    slug_Url: '@jhonny-c',
    nickname: '@jhonny-c',
});


....

Utilize el contexto Para compartir el 鈥渂ackend鈥. Tambi茅n cre茅 un m茅todo signUp para crear una cuenta.

function AuthProvider({ children }) {
const [newUser, setNewUser] = React.useState(publicData);
...

const signup = ({ username, nickname }) => {
	un  objeto que representa a un nuevo usuario
       const newSignUp = {
            name: username,
            slug_Url: '@' + nickname,
            nickname: '@' + nickname,
        }
        //Crear una copia del "backend"
        const createUser = [...newUser]
	//concatenar el nuevo usuario al "backend"
        createUser.push(newSignUp);
	//cambiar el "backend" para toda la app
        setNewUser(createUser);

//esta parte es para comprobar si el usuario existe
       
        const findUser = newUser.find(user => user.name === username);
        //el hook useLocation
        const from = location.state?.from?.pathname || -1;
//si el usuario no existe 
        findUser === undefined ?
//crearlo
        setUser({ name: username, nickname: nickname, role: roles.visitor })
//si existe es auth.user
        : setUser(findUser)
      //el hook useLocation
        navigate(from , { replace : true });

    }

 const auth = {  
        user, 
        ...
        newUser,
    }; 

La sub-ruta de ProfilePage es PublicProfile asi se ve desde App.js

function App () {
return(
...
<Route path="/profile"
         element={
        <ProfilePage/>
        }
        >

        <Route path=":slug" 
          element={
          <PublicProfile/>
          }
        />
      </Route>
...
)
}

Asi queda PublicProfile.js

function PublicProfile() {
   
    const { slug } = useParams();

   
    const { user, newUser}= useAuth();
  
   
    const profile =  newUser?.filter(person => person.slug_Url === slug);
  
    const verified = user === profile.name;


    return(
        <>
        {profile && !verified && profile.map( p => <ProfilesPub 
        props={p}
        />)
        }
        {profile && verified && profile.map( p => <Profiles 
        props={p}
        />)}
        </>
    );
}

//este componente es para cuando hace login  y le permite modificar el perfil
function Profiles({props}) {
 
    return(
        <>
        <div>
            <h1>Profile</h1>
            <h3>{props.name}</h3><button>editar</button>
            <div></div><button>editar</button>
            <div>Nickname: {props.nickname}</div><button>editar</button>
        </div>
        </>
    );
}
//este componente solo muestra el perfil p煤blico y no permite modificaciones.
function ProfilesPub({ props }) {
 
    return(
        <>
        <div>
            <h1>Profile</h1>
            <h3>{props.name}</h3>
            <div></div>
            <div>Nickname: {props.nickname}</div>
        </div>
        </>
    );
}

Reitero esta es una forma que no funciona. Desde LoginPage o SignUpPage se crea la sub-ruta directamente. Adem谩s, la sub-ruta solo es disponible despu茅s de hacer logout.

Condiciones:

  1. La persona debe estar autenticada.
  2. Si es due帽o de la cuenta; puede ver el bot贸n de edit profile en su perfil.
  3. Si el usuario est谩 autenticado y viaja a la ruta de otro perfil no podr谩 ver el bot贸n de edit profile, a menos que este usuario autenticado sea un admin.

Para realizar el reto:
Cambi茅 la ruta del componente <ProfilePage> a una ruta din谩mica:

 {
          path: "/profile/:slug",
          element:(
            <AuthRoute>
              <ProfilePage/>
            </AuthRoute>), 
        },

En mi custom hook useAuth.jsx tengo los roles de los usuarios y manejo el estado del username logueado. Los env铆o en el return del hook.


Ahora debo compartir estos datos al componente <Menu> y al componente <ProfilePage>; esto lo hago con el context del outlet y con las props para el <Menu>. Todos estos componentes est谩n contenidos en un 煤nico componente <Layout>

Bueno, ahora hay que decirle al men煤 que el link que lleva a ProfilePage es din谩mico; se har谩 de esta manera, uso una constante donde guardo la ruta con el dato din谩mico seg煤n el usuario logueado y luego se la paso como valor al par谩metro 鈥渢o鈥 del objeto que contiene la informaci贸n del link a ProfilePage.

Ahora finalmente en el componente <ProfilePage> se debe renderizar un bot贸n de edit profile dependiendo si el usuario es el due帽o de la cuenta o no. Se har铆a de esta manera:
Guardo el par谩metro y traigo los datos del contexto, luego, valido si el rol del usuario autenticado es un admin o no. Ahora con estos datos renderizo o no el saludo de la primera etiqueta p y la informaci贸n de la etiqueta button validando si es o no due帽o de la cuenta o admin el usuario autenticado gracias a la direcci贸n de la ruta.

As铆 se ve cuando es un usuario en su cuenta:

As铆 si ve si navega a otro perfil:

Espero haber entendido lo que se ped铆a en el reto. Pero queda algo pendiente, hacer que el bot贸n actualice datos. La cosa es que mi informaci贸n es hardcodeada en su mayor铆a ya que no uso un objeto literal que tenga todos esos datos y luego pueda cambiar sus propiedades, tampoco uso una API, base de datos o localstorage. Pero si tuviera la forma de cambiar los datos que est谩 debajo de Count information:

<h3>Count information</h3>
 <p><b>Interests:</b></p>
 <p>....</p>
 <p><b>Posts created:</b><span>{` ...`}</span></p>
 <p><b>Contributions:</b><span>{` ...`}</span></p>

Ser铆a actualizando un estado que contenga un objeto literal con la informaci贸n de ese perfil. La actualizaci贸n ser铆a con un componente formulario que actualice el estado y luego navegue al profilepage, el profilpage renderizar铆a datos nuevos.

Mi soluci贸n

Cuando Juan mencion贸 todo el reto, por mi mente pas贸 un 鈥淎aaaihhggg鈥 por d贸nde empiezo鈥︹
.
Les describir茅 c贸mo se fue creando mi soluci贸n en mi cabeza:
.

Cambiar estructura de profiles

Antes solo pod铆amos mirar nuestro perfil, ahora tenemos que hacer que cualquier perfil pueda ver cualquier otro perfil.
La ruta 鈥/profile鈥 contiene un input con un buscador de perfiles (hice un array con perfiles hardcodeados). La ruta 鈥/profile/:username鈥 contiene la vista del perfil del usuario.

<Route path="/profile/" element={<SearchProfilePage />} />
<Route path="/profile/:username" element={<ProfilePage />} />

.

Aplicar l贸gica de b煤squeda y navegaci贸n

En el buscador de perfiles, verifico si el username ingresado en el input existe; si no existe, lanzo un alert(), si s铆 existe, navego hacia la ruta 鈥/profile/${username}鈥

function SearchUserPage() {
	const navigate = useNavigate();
	const [username, setUsername] = useState("");

	const searchUser = (ev) => {
		ev.preventDefault();

		if (!users.some((user) => user.username === username)) {
			alert("This user does not exist. Try another user.");
			return;
		}

		navigate(`/profile/${username}`);
	};

	return (
		<>
			<h2>Search User</h2>

			<form onSubmit={searchUser}>
				<label htmlFor="username">Type an username</label>
				<input
					type="text"
					name="username"
					id="username"
					value={username}
					onChange={(ev) => setUsername(ev.target.value)}
				/>
				<button type="submit">Search</button>
			</form>
		</>
	);
}

.

Renderizar y Autorizaci贸n en Profile Page

En Profile Page, recojo el user autenticado y el user enviado por la URL.
Si los dos son el mismo user, tiene permiso para editar el perfil.
Si el user autenticado tiene el rol de admin, puede editar el perfil.

function ProfilePage() {
	const { user } = useAuth();
	const params = useParams();

	const data = users.find((user) => user.username === params.username);

	const canEditProfile = () => {
		if (user.role.name === "admin") return true;
		if (user.username === data.username) return true;

		return false;
	};

	return (
		<>
			<h1>ProfilePage</h1>

			<h2>{data.username}</h2>
			<img src={data.img.src} alt={data.img.alt} width="400" />
			<p>{data.bio}</p>
			{canEditProfile() && <button>Edit Profile</button>}
		</>
	);
}

.
.

Mi filosof铆a para este reto

Lo que hago es simular una base de datos teniendo un array de usuarios en un archivo aparte, y en cada b煤squeda o autenticaci贸n, lo consulto. Y dentro de cada componente hago la l贸gica necesaria trabajando los datos que tengo: array de usuarios y user autenticado (o no autenticado)

Saludos, playzinautas

les comparto un proyecto que hice con los retos de las clases, me faltan detalles por mejorar, pero espero con ansias su feedback
Repo.
GitHub Page.

usuario admin clave admin123
usuario con role creator
juandc clave admin123

usuarios con role editor
nicobytes clave admin123
diannerd clave123

se me ocurri贸 separar los componentes del blog, y hacer lo mismo que hicimos en las rutas cuando usamos 鈥淎uthRoute鈥 pero lo tome como un AuthAccess con el valido los posibles roles y ya s茅 que componente mostrar y cual no, pero me parece que se complica ya que se debe generar uno para cada componente

Aqu铆 mi repositorio de lo que hice se pueden editar y eliminar los blogs solo si tienes los permisos se puede acceder a el de forma publica cree un custom hook para la info de los blogs, acciones y comparto la informaci贸n en base a los patrones de render del curso anterior no se si es exactamente lo que se buscaba pero me gusto como quedo

Buenas, me demore un poco pero lo logre.
Le puse algo de amor con bootstrap 鉂わ笍 My Blog