From 85b55660f9df6f511bfcc25d9a4781d8406d9b91 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 29 Jan 2025 22:50:53 -0500 Subject: [PATCH] feat: Update user profile handling and enhance public user details response --- CONTRIBUTING.md | 83 ++------ backend/server/.env.example | 4 +- backend/server/main/urls.py | 2 +- backend/server/users/views.py | 26 ++- frontend/src/lib/components/Avatar.svelte | 4 +- frontend/src/locales/en.json | 6 +- frontend/src/routes/profile/+page.server.ts | 29 --- frontend/src/routes/profile/+page.svelte | 112 ----------- .../src/routes/profile/[uuid]/+page.server.ts | 40 ++++ .../src/routes/profile/[uuid]/+page.svelte | 180 ++++++++++++++++++ 10 files changed, 274 insertions(+), 212 deletions(-) delete mode 100644 frontend/src/routes/profile/+page.server.ts delete mode 100644 frontend/src/routes/profile/+page.svelte create mode 100644 frontend/src/routes/profile/[uuid]/+page.server.ts create mode 100644 frontend/src/routes/profile/[uuid]/+page.svelte diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c980b88..7b49b3d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,91 +1,50 @@ # Contributing to AdventureLog -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. +We’re excited to have you contribute to AdventureLog! To ensure that this community remains welcoming and productive for all users and developers, please follow this simple Code of Conduct. ## Pull Request Process -1. Please make sure you create an issue first for your change so you can link any pull requests to this issue. There should be a clear relationship between pull requests and issues. -2. Update the README.md with details of changes to the interface, this includes new environment - variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer to merge it for you. +1. **Open an Issue First**: Discuss any changes or features you plan to implement by opening an issue. This helps to clarify your idea and ensures there’s a shared understanding. +2. **Document Changes**: If your changes impact the user interface, add new environment variables, or introduce new container configurations, make sure to update the documentation accordingly. The documentation is located in the `documentation` folder. +3. **Pull Request**: Submit a pull request with your changes. Make sure to reference the issue you opened in the description. ## Code of Conduct ### Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +At AdventureLog, we are committed to creating a community that fosters adventure, exploration, and innovation. We encourage diverse participation and strive to maintain a space where everyone feels welcome to contribute, regardless of their background or experience level. We ask that you contribute with respect and kindness, making sure to prioritize collaboration and mutual growth. ### Our Standards -Examples of behavior that contributes to creating a positive environment -include: +In order to maintain a positive environment, we encourage the following behaviors: -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members +- **Inclusivity**: Use welcoming and inclusive language that fosters collaboration across all perspectives and experiences. +- **Respect**: Respect differing opinions and engage with empathy, understanding that each person’s perspective is valuable. +- **Constructive Feedback**: Offer feedback that helps improve the project and allows contributors to grow from it. +- **Adventure Spirit**: Bring the same sense of curiosity, discovery, and positivity that drives AdventureLog into all interactions with the community. -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting +- Personal attacks, trolling, or any form of harassment. +- Insensitive or discriminatory language, including sexualized comments or imagery. +- Spamming or misusing project spaces for personal gain. +- Publishing or using others’ private information without permission. +- Anything else that could be seen as disrespectful or unprofessional in a collaborative environment. ### Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +As maintainers of AdventureLog, we are committed to enforcing this Code of Conduct and taking corrective action when necessary. This may involve moderating comments, pulling code, or banning users who engage in harmful behaviors. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +We strive to foster a community that balances open collaboration with respect for all contributors. ### Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies in all spaces related to AdventureLog. This includes our GitHub repository, discussions, documentation, social media accounts, and events—both online and in person. ### Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +If you experience or witness unacceptable behavior, please report it to the project team at `contact@adventurelog.app`. All reports will be confidential and handled swiftly. The maintainers will investigate the issue and take appropriate action as needed. ### Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +This Code of Conduct is inspired by the [Contributor Covenant](http://contributor-covenant.org), version 1.4, and adapted to fit the unique spirit of AdventureLog. diff --git a/backend/server/.env.example b/backend/server/.env.example index 4c1f9ad..598aeb7 100644 --- a/backend/server/.env.example +++ b/backend/server/.env.example @@ -25,10 +25,10 @@ EMAIL_BACKEND='console' # ------------------- # # For Developers to start a Demo Database -# docker run --name postgres-admin -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=admin -p 5432:5432 -d postgis/postgis:15-3.3 +# docker run --name adventurelog-development -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=adventurelog -p 5432:5432 -d postgis/postgis:15-3.3 # PGHOST='localhost' -# PGDATABASE='admin' +# PGDATABASE='adventurelog' # PGUSER='admin' # PGPASSWORD='admin' # ------------------- # \ No newline at end of file diff --git a/backend/server/main/urls.py b/backend/server/main/urls.py index b7bb2a1..60ce08e 100644 --- a/backend/server/main/urls.py +++ b/backend/server/main/urls.py @@ -22,7 +22,7 @@ urlpatterns = [ path('auth/is-registration-disabled/', IsRegistrationDisabled.as_view(), name='is_registration_disabled'), path('auth/users/', PublicUserListView.as_view(), name='public-user-list'), - path('auth/user//', PublicUserDetailView.as_view(), name='public-user-detail'), + path('auth/user//', PublicUserDetailView.as_view(), name='public-user-detail'), path('auth/update-user/', UpdateUserMetadataView.as_view(), name='update-user-metadata'), path('auth/user-metadata/', UserMetadataView.as_view(), name='user-metadata'), diff --git a/backend/server/users/views.py b/backend/server/users/views.py index b03760e..e741a69 100644 --- a/backend/server/users/views.py +++ b/backend/server/users/views.py @@ -11,6 +11,8 @@ from django.shortcuts import get_object_or_404 from django.contrib.auth import get_user_model from .serializers import CustomUserDetailsSerializer as PublicUserSerializer from allauth.socialaccount.models import SocialApp +from adventures.serializers import AdventureSerializer, CollectionSerializer +from adventures.models import Adventure, Collection User = get_user_model() @@ -79,12 +81,28 @@ class PublicUserDetailView(APIView): }, operation_description="Get public user information." ) - def get(self, request, user_id): - user = get_object_or_404(User, uuid=user_id, public_profile=True) + def get(self, request, username): + print(request.user) + if request.user.username == username: + user = get_object_or_404(User, username=username) + else: + user = get_object_or_404(User, username=username, public_profile=True) + serializer = PublicUserSerializer(user) + # remove the email address from the response user.email = None - serializer = PublicUserSerializer(user) - return Response(serializer.data, status=status.HTTP_200_OK) + + # Get the users adventures and collections to include in the response + adventures = Adventure.objects.filter(user_id=user, is_public=True) + collections = Collection.objects.filter(user_id=user, is_public=True) + adventure_serializer = AdventureSerializer(adventures, many=True) + collection_serializer = CollectionSerializer(collections, many=True) + + return Response({ + 'user': serializer.data, + 'adventures': adventure_serializer.data, + 'collections': collection_serializer.data + }, status=status.HTTP_200_OK) class UserMetadataView(APIView): permission_classes = [IsAuthenticated] diff --git a/frontend/src/lib/components/Avatar.svelte b/frontend/src/lib/components/Avatar.svelte index 01eb068..4bae036 100644 --- a/frontend/src/lib/components/Avatar.svelte +++ b/frontend/src/lib/components/Avatar.svelte @@ -34,7 +34,9 @@ ? `${user.first_name} ${user.last_name}` : user.username}

-
  • +
  • + +
  • diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 2b989e2..0a9f943 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -326,7 +326,11 @@ "new_password": "New Password (6+ characters)", "both_passwords_required": "Both passwords are required", "reset_failed": "Failed to reset password", - "or_3rd_party": "Or login with a third-party service" + "or_3rd_party": "Or login with a third-party service", + "no_public_adventures": "No public adventures found", + "no_public_collections": "No public collections found", + "user_adventures": "User Adventures", + "user_collections": "User Collections" }, "users": { "no_users_found": "No users found with public profiles." diff --git a/frontend/src/routes/profile/+page.server.ts b/frontend/src/routes/profile/+page.server.ts deleted file mode 100644 index 825a867..0000000 --- a/frontend/src/routes/profile/+page.server.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad, RequestEvent } from '../$types'; -const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; - -export const load: PageServerLoad = async (event: RequestEvent) => { - const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; - if (!event.locals.user || !event.cookies.get('sessionid')) { - return redirect(302, '/login'); - } - - let sessionId = event.cookies.get('sessionid'); - let stats = null; - - let res = await event.fetch(`${endpoint}/api/stats/counts/`, { - headers: { - Cookie: `sessionid=${sessionId}` - } - }); - if (!res.ok) { - console.error('Failed to fetch user stats'); - } else { - stats = await res.json(); - } - - return { - user: event.locals.user, - stats - }; -}; diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte deleted file mode 100644 index 31fdfb6..0000000 --- a/frontend/src/routes/profile/+page.svelte +++ /dev/null @@ -1,112 +0,0 @@ - - -
    -
    - - {#if data.user.profile_pic} -
    -
    - Profile -
    -
    - {/if} - - - {#if data.user && data.user.first_name && data.user.last_name} -

    - {data.user.first_name} - {data.user.last_name} -

    - {/if} -

    {data.user.username}

    - - - {#if data.user && data.user.date_joined} -
    -

    {$t('profile.member_since')}

    -
    - -

    - {new Date(data.user.date_joined).toLocaleDateString(undefined, { timeZone: 'UTC' })} -

    -
    -
    - {/if} -
    - - - {#if stats} -
    - -

    - {$t('profile.user_stats')} -

    - -
    -
    -
    -
    {$t('navbar.adventures')}
    -
    {stats.adventure_count}
    -
    - -
    -
    {$t('navbar.collections')}
    -
    {stats.trips_count}
    -
    - -
    -
    {$t('profile.visited_countries')}
    -
    - {Math.round((stats.visited_country_count / stats.total_countries) * 100)}% -
    -
    - {stats.visited_country_count}/{stats.total_countries} -
    -
    - -
    -
    {$t('profile.visited_regions')}
    -
    - {Math.round((stats.visited_region_count / stats.total_regions) * 100)}% -
    -
    - {stats.visited_region_count}/{stats.total_regions} -
    -
    - -
    -
    {$t('profile.visited_cities')}
    -
    - {Math.round((stats.visited_city_count / stats.total_cities) * 100)}% -
    -
    - {stats.visited_city_count}/{stats.total_cities} -
    -
    -
    -
    - {/if} -
    - - - Profile | AdventureLog - - diff --git a/frontend/src/routes/profile/[uuid]/+page.server.ts b/frontend/src/routes/profile/[uuid]/+page.server.ts new file mode 100644 index 0000000..c1abfce --- /dev/null +++ b/frontend/src/routes/profile/[uuid]/+page.server.ts @@ -0,0 +1,40 @@ +import { redirect, error } from '@sveltejs/kit'; +import type { PageServerLoad, RequestEvent } from '../../$types'; +const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; + +export const load: PageServerLoad = async (event: RequestEvent) => { + const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; + + let uuid = event.params.uuid as string; + + if (!uuid) { + return error(404, 'Not found'); + } + + // let sessionId = event.cookies.get('sessionid'); + // let stats = null; + + // let res = await event.fetch(`${endpoint}/api/stats/counts/`, { + // headers: { + // Cookie: `sessionid=${sessionId}` + // } + // }); + // if (!res.ok) { + // console.error('Failed to fetch user stats'); + // } else { + // stats = await res.json(); + // } + + let userData = await event.fetch(`${endpoint}/auth/user/${uuid}/`); + if (!userData.ok) { + return error(404, 'Not found'); + } + + let data = await userData.json(); + + return { + user: data.user, + adventures: data.adventures, + collections: data.collections + }; +}; diff --git a/frontend/src/routes/profile/[uuid]/+page.svelte b/frontend/src/routes/profile/[uuid]/+page.svelte new file mode 100644 index 0000000..d2d784d --- /dev/null +++ b/frontend/src/routes/profile/[uuid]/+page.svelte @@ -0,0 +1,180 @@ + + +
    +
    + + {#if user.profile_pic} +
    +
    + Profile +
    +
    + {:else} + +
    +
    + {#if user.first_name && user.last_name} + Profile + {:else} + Profile + {/if} +
    +
    + {/if} + + + {#if user && user.first_name && user.last_name} +

    + {user.first_name} + {user.last_name} +

    + {/if} +

    {user.username}

    + + + {#if user && user.date_joined} +
    +

    {$t('profile.member_since')}

    +
    + +

    + {new Date(user.date_joined).toLocaleDateString(undefined, { timeZone: 'UTC' })} +

    +
    +
    + {/if} +
    + + + + + +
    + +

    + {$t('auth.user_adventures')} +

    + + {#if adventures && adventures.length === 0} +

    + {$t('auth.no_public_adventures')} +

    + {:else} +
    + {#each adventures as adventure} + + {/each} +
    + {/if} + + +
    + +

    + {$t('auth.user_collections')} +

    + + {#if collections && collections.length === 0} +

    + {$t('auth.no_public_collections')} +

    + {:else} +
    + {#each collections as collection} + + {/each} +
    + {/if} +
    + + + {user.first_name || user.username}'s Profile | AdventureLog + +