Code Monkey home page Code Monkey logo

backend-capstone's Introduction

Hi there, I'm Anca 👋

I'm a software developer who is passionate about ensuring applications solve users’ problems and bring value.

You can check out my portfolio here and find me on LinkedIn as well.
💻 The ability to help solve big - and small - problems is what has drawn me to the software development role. A few years ago, I transitioned from a technical writer into a business analyst role. Then, in January 2020, I was excited to make the commitment to complete the Nashville Software School Web Developer Bootcamp. It turned out to be the best decision I made - in a year otherwise full of surprises! I graduated from NSS on January 12, 2021 and am actively searching for an organization where I can combine my previous experience and new technical skills to make meaningful contributions.
💁🏻‍♀️ In addition to my technical skills, I bring a wide variety of soft skills and business experience to the table. As a former liberal arts major, I have strong communication skills that have served me well throughout my professional career. As a technical writer, I ensured user adoption of new features by translating “dev speak” into meaningful documentation. As a business analyst, I collaborated with all roles to ensure our products, our teams, and our customers were successful.
🎯 I recognize that the arc that starts with the backend developer and the UI team stretches through customer support and sales into the user’s routines and that a better understanding of this continuum makes for more effective and impactful software.

Past Projects

  • December 2020: My final capstone project at the Nashville Software School: Unlimited - A full-stack CRUD application built with C#, ASP .NET Core MVC, SQL database, HTTP-based APIs, and a single-page React application with Firebase authentication. A one-stop shop where parents of young budding musicians can create custom practice plans, browse games from the database, filter them based on their child's age and instrument, and track progress. It will make them want to practice!
  • Summer 2020: GearUp!, my frontend capstone project - take a quick look, and you will be able to tell I was so excited to work on this project!

backend-capstone's People

Contributors

ancasimon avatar

Watchers

 avatar  avatar

backend-capstone's Issues

ERD & Mockups

Latest ERD (updated Nov 30):
Screen Shot 2020-11-30 at 7 15 36 PM

For mockups - See document attached below.

MVP: Edit game status on a Practice Plan

User Story

As an authenticated user, I can mark a game on a practice plan as completed.

Dev Notes

SQL

  1. Write the UPDATE statement to update the IsCompleted property on a PracticePlanGame record to 1 / true when the ID matches the ID passed in.

API

  1. We should already have an Update method in the Repo and Controller files for a single practiceplangame record after the API ticket.

React project

  • In the SinglePracticePlanView,js file:
  1. Pass the buildPracticePlan function down from the SinglePracticePlanView page as props to the PracticePlanGameItem component .
  • In PracticePlanGameItem.js:
  1. In html > Add a Mark Complete check box.
  2. Declare a variable in state for the completed status and get it from the isCompleted property on the practice plan game object passed into here via props.
  3. Write the changePracticeCompleted function that sets the state of this variable to the checked flag on e.target. This function also builds the updated practice plan game object - with data from the object that came in props - except for the Completed check box - whose value comes from e.target - and then passes this object in a call to update the practicePlanGame using the update function in the corresponding data file. When successful, this call also refreshes the page.
  4. Call the change function you just wrote in the onChange tag in the check box input tag.
  5. Be sure to add the checked tag that uses the value of this variable from state.

MVP: Edit Practice Plan Details

User Story

As a user, I can edit details about a practice plan such as its name and dates.

Dev Notes

  1. Leveraging the new practice plan component -will use the same component for editing. Note: This was hard - it seems to have worked out in the end but multiple times while I was doing I had the thought that I should just stop and duplicate the form code in two components and be done with it. In the end, I made it to work - but it took a number of ternaries and being very precise about what I set in state and using that and resetting that appropriately when needed!

API

  1. Update the PUT method for a practice plan to use the userID determined via the Firebase ID that we have access to on the backend via the FirebaseEnabledController - and use that userId to set the userId property on a practicePlan. Pass in the userId as a parameter in the Update method in the Repository.

React frontend

  • In the PracticePlanNew.js file:
  1. In state: -1- declare a practicePlanId variable and set it to this.props.match.params.practiceplanid * 1 - since otherwise it comes across as a string. -2- Also declare a variable for newRecordForm set to true by default - we will set it to false if we find a practice plan ID in params or set it to true again if we just find an undefined value in that case. -3- Also declare an empty array of games. -4- Declare variables for all the practice plan data.
  2. Add a function to get the practice plan ID from this.props.match.params.practiceplanid - if it is undefined, then we know we are on a new form so we will set this to 0 - otherwise, we reset the state to this value and call the function that gets its data - see next step below.
  3. Add a function to get the details of the practice plan using this ID - and this function gets triggered only when that ID has a real value - and if successful, have the function set the values for the practice plan properties variables in state.
  4. When building the page, get the practice ID (and then its details if it's a real value) and also get the list of games and set the state of that array.
  5. Add a function for saving an updated version of the practiceplan record. Build out the updated practice plan usign data for its properties from state.When successful, have it return the user to the single view for that practice plan.
  6. Update the function that created a practice plan game to refresh this page instead of sending the user to the single view page.
  7. in the PracticePlanGameItem component we use to build out the list of practice plan games - pass in the function that rebuilds this practice plan edit/new page as a refreshPage tag - and update the tag in the same component when it is used called on the SinglePracticePlanView page. (update just the tag/attribute name!! We are passing in different refresh functions since we are calling that component from different places and we want it to refresh accordingly!)
  8. Last - user ternaries in the HTML to change the labels and default values so that they are appropriate for the Edit or the New version of this page - use the newRecordForm variable from state to determine which options to show.
  9. In the html, add the grid headers for the practice plan games associated with this practice plan and call the PracticePlanGameItem component.
  10. Add a Cancel button on the New/Edit form.

React/API: Set up authentication

As a user, I want to be able to log in securely to the app.

Dev Notes

Backend:

  1. Startup.cs file > Configure method > Add App.UseAuthorization(); — already there (around line 52) tells the app that this app has authentication.
    ere’s user info in this request
  2. Install JWTBearerDefaults package in the Package Manager Console: PASTE the Install command in the Package Manager Console: Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 3.1.10
  3. Startup.cs file > The using statement we need: using Microsoft.AspNetCore.Authentication.JwtBearer;
  4. Startup.cs file > ConfigureServices method > services.AddAuthentication - we are telling it to pull user info from the request and this is where we tell it how to pull that user info: - it is saying: we will be using authentication and we want to use the JWT authentication scheme.
  5. Next: .AddJwtBearer - This part says: we are going to be using JWTBearer and they will come in as a JWT token - this is where the token will be
  6. Next: set up configuration options for our token:
  7. IncludeErrorDetails - this says that if the authentication fails, let us know why.
  8. Authority: https://securetoken.google.com/fish-store-a71e6 - this says who is in charge of issuing the token - for us Firebase will be issuing the token.
  9. TokenValidationParameters - this is about how the token should be validated in the first place -
    —ValidateLifetime - has the token outlived how long it will be valid for
    —ValidateAudience
    —ValidateIssuer - making sure the person who issues the token matches who we expect - same as authority: https://securetoken.google.com/fish-store-a71e6
    —ValidAudience - we can specify who the audience will be — thesis thee second part froth Firebase Webb project ID - from Firebase!! - fish-store-a71e6 - this is also what needs to be replaced when we create our own project!!
  10. Firebase project ID: This part is always the same: https://securetoken.google.com/; the second part is project-specific!!
  11. NEXT: Add an attribute called [Authorize] ( confirm it added a using statement! For Microsoft.AspNetCore.Authorization)
    — this says; in order to access any of the methods in this Repository file, you have to be authenticated
    — if you try to make one of those API calls without being authenticated > you get a 401 - Unauthorized call
    Controller file: add the [Authorize] attribute to the method - which specifies that the user has to be authenticated to get the results of the method - you can add it to some methods but not all methods if you want - or to all the methods run a file by adding it for the top class of that file.
    --You can also say at the Controller level - that the full controller requires authorization except a given method - and for that, you add the [AllowAnonymous] attribute! We are excluding this one method from the [Authorize] attribute!

Frontend Auth:

  1. See steps in Auth document!

Specific additions to ensure we connect the Firebase user record and the database user record - Dev Notes:

API:

  1. I ended up creating a GetUserIdByUid method just for testing purposes - I am planning to use the UID inside the SQL methods to get user-specific data.
OLDER NOTES
  1. UserRepository: Write the GetUserIdByUid(string uid) method which uses a sql query that finds the database user ID based on the UID passed in and uses ExecuteScalar method to return the user ID.
  2. UsersController: Write the corresponding GetUserIdByUid(string uid) method.
  3. any Controller for a collection that needs user id to get data: add a _userRepo here: declare it at the top of the class and add it in its constructor; Update the method to get data to use the uid as the parameter; Declare a currentUserId variable that is set by calling the GetUserIdByUid from the UserRepository and then call the method to get data from the appropriate Repository and pass in that new variable as the userId.
  4. Add the FirebaseEnabledController. Make sure the UsersController and any other Controller using the UID inherits from it!!

React project:

  1. I am planning to use the UID inside the SQL methods to get user-specific data.
OLDER NOTES BELOW:
  1. helpers> data folder > usersData > add a new getSingleUserIdByUid function
  2. Any js file where you need the userId > in the appropriate function -1- declare a new variable for the loggedInUserUid and get that uid by calling the authData.getUid() method already written in the authData file; -2- then call the new getSingleUserIdByUid method and pass in this uid; -3- then pass in the userId returned to the function where you need it!

MVP: User Profile Page

User Story

As an authenticated user, I can view my User Profile page.

Dev Notes

  1. Note: We should already have a GetSingleUser method in the API from the API ticket.
  2. Note: We should also have a GetUserByUid method.
  3. React project > App.js > if not there already, add the Profile private route.
  4. React project > MyNavbar.js > if not there already, add the Profile navbar menu.
  5. React project > components > pages > Create a Profile folder with js and scss files. In componentDidMount(), get the user by uid. Write out the HTML to display all the data you want from the database user record.

Users can upload files for photos in games.

Dev Notes

Steps to upload a file:

Backend:

  1. Create the Controller with an Add and Get method for the single file.
  2. Create the repository with the corresponding methods

SQL database:

  1. Create the new table - if not there already
  2. Add a column to another table if you need a foreign key relationship - if the file goes with another existing record type.

Frontend:

...

MVP: Make all game names hyperlink

User Story

As a user, whenever I see a game on a page in the app, I want to be able to click on the game and go to the Single Game View page.

Dev Notes

React project:

  1. Places where games are displayed besides the main Games page which was covered in its own ticket: Submissions page, Practice Plan view/edit page.
  2. Import the SingleGameView component / add it as a Link in the row for each game on these pages.

MVP: Delete Game from Single Practice Plan Details Page

User Story

As a user, I want to be able to delete a game I previously selected from one of my practice plans.

Dev Notes

SQL

  1. Write the script to update a practice plan game record.

API

  1. In the PracticePlanGameRepository file and in the PracticePlanGamesController file, add methods to update the properties of a practicePlanGame object based on the id passed in and the updated object passed in.
  2. Do the same with methods to get a practicePlanGame by ID so that you can test your work.

React app

  1. In the practicePlanGamesData.js file > add an update function.
  2. In the PracticePlanGameItem.js file > add the Inactivate function which inactivates a practicePlanGame record when the user wants to "delete" it. 1-- above the render. -1-Declare an object here for the practicePlanGame ID ; 2- Call the updatePracticePlanGame function from the data file; -3- In the then() here, rebuild the single practice view page in order to refresh the data and display only remaining games / remove the one the user chose to delete.-- to do this, pass the buildSingleView function in props from the SInglePracticePlanView page to the individual practice plan game record as well as the ID of the practice plan you were on!!
  3. In the SinglePracticePlanView.js file: - Add a Remove button on the row for each game (probably will display these in a list). ; - Add the onClick event handler with the removePracticePlanGameItem function.

MVP: Remove game from practice plan - already done in #31

User Story

As an authenticated user, I can remove a previously selected game from one of my practice plans while on the Single Practice Plan View page.

Dev Notes

SQL

  1. Write the statement to update the IsActive status to 0 / false for the practice plan game record with the ID that gets passed in.

API

  1. In the PracticePlanGameRepository file > write the DeletePracticePlanGame method which uses the SQL UPDATE statement above.
  2. In the PracticePlanGamesController file > write the corresponding method.

React project:

  • In the practicePlanGamesData.js > write the updatePracticePlanGame function
  • In the SinglePracticePlanView.js file:
  1. Add a Remove button on the row for each game (probably will display these in a list).
  2. Add the onClick event handler with the removePracticePlanGameItem function.
  3. Write the removePracticePlanGameItem function above the render. -1-Declare an object here for the practicePlanGame ID --which we get via the e.target data!!! since we are clicking on the row for that specific record-2- Call the updatePracticePlanGame function from the data file; -3- In the then() here, rebuild the single practice view page in order to refresh the data and display only remaining games / remove the one the user chose to delete.

SQL: Seed data and insert scripts

MVP: Create Practice Plan Page

User Story

As an authenticated user, I can create a practice routine plan that includes selected games from the database from the Practices page in the app.

Dev Notes

Notes:

  1. We are creating a PracticePlan record.
  2. We are also creating PracticePlanGames records - which use the ID of the practice plan created in step 1 above AND the id of the user logged in (get this based on the UID).

SQL

  1. Write SQL scripts to insert and update PracticePlan records based on the user ID coming in.
  2. Write SQL scripts to insert and update PracticePlanGame records based on the practice plan from the previous step and the game id coming in.

API

  1. PracticePlanRepository > Add a new method to create a new PracticePlan record by using the userId.
  2. PracticePlansController > -1- Instantiate a userRepo here; -2- Add the corresponding method for the Add a single practice plan - using the user ID you get by using the Firebase UID you can get via the FirebaseEnabledController.
  3. PracticePlanGameRepository > Add a new method to POST a new PracticePlanGame record by using the data passed in from the front end.
  4. PracticePlanGamesController > Add the corresponding method for the Add a practice plan game.

React Frontend

  1. practicePlansData.js > Write the POST methods for creating a single practice plan.
  2. PracticePlans.js > In the HTML in the return > add the Create Practice Plan button > Add the onClick tag to this button, which calls the createPracticePlan function. Above the render, define the createPracticePlan function that gets called when the user clicks Create Practice Plan
  3. PracticePlanNew.js > In the HTML in the return > add the form with all the fields for the properties you want to capture when users create a new practice plan - add an ID, a placeholder, a value and onChange tag to each field. ALSO add the Save New Practice Plan button.
  4. Above the render, define the change... functions to update the value properties with the value from the target after a change is made - for all the form fields for practice plan info needed.
  5. Above the render, define the savePracticePlan function that gets called when the user clicks Save New Practice Plan: This function takes e as an argument, calls e.preventDefault(), creates a new PracticePlan record - passing in the userId.
  6. NEXT: Get a list of all available games and display their name in a drop down! Once the user selects a game > open a modal window with all the practice game-specific fields; when user saves the new game added, take the user to the Single Practice Plan View page for this new practice plan. (using the this.props.history.push to change the url!!)

Deploy site

Deployed frontend with Firebase - using Firebase Hosting services
Copied local SQL database to an Azure SQL database
Deployed backend with Azure

MVP: User Registration

User Story

As a visitor to the site, I can register on the web site and log in to take advantage of additional website features.

Dev Notes

  1. API: Add a function in the UserRepository to add a user - if not already there after the initial API ticket!!
  2. Firebase: Create a project in Firebase and enable the Email/Password sign in method.
  3. React: Create a Register folder with Register.js and Resgister.scss
  4. React > components > pages > Register > Register.js: -1- declare all the variables that are used as values in the registration fields in state (first name, last name, email, password, photourl); -2-Create a Registration button in the login form; -3-Create a click event for the registration button and define the function; -4-add functions to change variables in state based on the data the user enters in the registration fields - which are called by the onChange tag in those fields.
  5. React > helpers> data> authData.js > add the registerUser function by creating an object for properties need from the User model: email, password, username, firstname, lastname, photoUrl, firebaseUid!!!.

MVP: Games Page

User Story

As a user, I want to see a list of active / not-deleted games that display some basic metadata (instrument, age range, song, prework level, who submitted it).

Dev Notes:

SQL

Note: we should already have the GetAllGames() method written after the API ticket.

  • Update the sql query that gets the games' data we need on the UI page so that it:
  1. Selects the columns we want to display: Instrument, Age, PreWork Level, Song, SubmittedBy.
  2. gets data from the Games table,
  3. then joins the Users table on the user ID in order to get the user data,
  4. AND THEN filters the Games table by the IsActive == true property.

API

Create a new model that contains all the data you will be getting from SQL - including the first and last name of the user.

React Frontend

  1. In helpers > data > create a gamesData.js file and write the viewAllGames function, which is a new Promise and does an axios call to get all the games from our database /from the api/games endpoint.
  2. In src > components > pages > create a Games folder with .js and .scss files.
  3. In the Games.js > declare a state object and in it, declare an empty array of games.
  4. Import the gamesData.js file.
  5. Write the getGames method that calls the corresponding method from the gamesData.js in order to get the array of games - this is a Promise, so it takes a .then a(where we set the state for the array of games) and a .catch (where we console the error).
  6. Add the componentDidMount() method, which calls the getGames method upon load.
  7. In the render here > declare the allGames object that takes the value from state; - write a buildGames function that takes the games' array and maps over it and for each game, calls the GameItem component, uses a key attribute equal to game.id and also has a game attribute equal to the game object.
  8. In the return here > add the html for the page and the div that will be the flex container for the cards.
  9. In src > components > shared > create a GameItem folder with .js and .scss files.
  10. In the GameItem.js file > define the HTML for how each game care should look and display the required metadata. USE reactstrap from the beginning!!!
  11. In the Games.js file > import the new GameItem.js file.

Validate user registration

Validate user registration:

  1. a user with an email address already in the system cannot register again with the same email address;
  2. an email has to have a valid email format
  3. a user cannot register without specifying first name, last name, email, and password

MVP: Single Practice Plan Details Page

User Story

As a user, when I click on a View Details link on a practice plan on the My Practice Plans page, then I go to the single practice plan details page where I see all the details about a practice plan ( a list of the games I included and the date and notes for each game).

SQL

  1. Write the query to get a single practice plan by id.
  2. Join the PracticePlanGames table to get the details of a practice plan.
  3. Need to join the Games table so that we can get the name for each game.

API

  1. Create a new model that includes the game name.
  2. Create a new repository for the new model.
  3. Write the methods to get a single practice plan by id in the new PracticePlanWithGameNameRepository and the PracticePlansController files.

React Frontend

  • In App.js > if not there already, add a new route for the single practice plan view URL. This tells the program at which URL to render the data it puts together after making the axios call and using the component to determine how to display that data.

  • In the practicePlansData.js file > import axios; import the baseUrl from the constants.json file > then write the getSinglePracticePlan method. The URL here tells the program from which URL to get the data it needs.

  • In the PracticePlanItem.js shared component, add a link that takes the user to the single practice plan view page: -1-import the Link component from react-router-dom package; -2- in the render, declare a singlePracticePlanLink variable for the URL path to the single view; -3- in the HTML, add a Link component with the to attribute being equal to the singlePracticePlanLink variable you just defined for the single view path.

  • In components > pages > create a SinglePracticePlanView folder with corresponding js and scss files. In the js file:

  1. Declare the state object with a selectedPracticePlan as an empty object and a selectedPracticePlanId as the id we grab from this.props.match.params.id (aka the id in the url).
  2. Write a buildSingleView function that: -1- declares the practicePlanId object and grabs the id from state; -2-calls the getSinglePracticePlan function from the practicePlansData.js file and passes in the practicePlanId; -3-in the then(), takes the response and sets the state for the practice plan object to the response.data coming back.
  3. Add componentDidMount() method, which grabs the practicePlanId again from state and calls the buildSingleView function - which takes the practicePlanId as an argument.
  4. In the render here: set the practicePlan to what is in state.
  5. In the return, write out the html for how the single practice plan view page should look and the practice plan data that should get displayed here.
  6. Add a Close button that is actually a link that takes the user to the My Practice Plans page.

MVP: Delete practice plan

User Story

As an authenticated user, I can soft-delete a practice plan I have created from the Single Practice Plan View page.

Dev Notes

SQL

  1. Write the statement to update the IsActive status to 0 / false for the practice plan with the ID passed in and for the PracticePlanGame records with that ID too!!!

API

  1. In the PracticePlanRepository file > write the DeletePracticePlanAndRelatedGames method which uses the SQL UPDATE statement above.
  2. In the PracticePlansController file > write the corresponding method.

React project:

  • In the practicePlansData.js > write the updatePracticePlan function
  • In the SinglePracticePlanView.js file:
  1. Add a Delete button.
  2. Add the onClick event handler with the removePracticePlanItem function.
  3. Write the removePracticePlan function above the render. -1-Declare an object here for the item ID -- NOTE: For the object inside this function, we can get the id from the url / params!!! Ex: const { practicePlanId } = this.props.match.params;; -2- Call the updatePracticePlan function from the data file; -3- In the then() here, redirect the user to the Practices page - using the this.props.history.push(URL!).

MVP: Single Game Details Page

User Story

As a user, when I click on a View Details link on a game on the Games page, then I go to the single game details page where I see all the details about a game (name, instrument, song, age, pre-work level, prework, description, instructions, credit, website, iconUrl, submittedBy).

SQL

  1. We should already have the query to get a single game by id in the GamesRepository - after the API ticket.
  2. Need to join the Users table so that we can get the first and last name of the user who submitted the game.

API

  1. We should already have the appropriate methods to view a single product by ID in the GamesRepository and the GamesController.
  2. Create a new model that includes the SubmittedByFirstName and SubmittedByLastName.
  3. Write a method in the repository to get that data.
  4. Update GetGameById method in the Controller to return this new model.

React Frontend

  • In App.js > add a new route for the single product view URL. This tells the program at which URL to render the data it puts together after making the axios call and using the component to determine how to display that data.

  • In the gamesData.js file > import axios; import the baseUrl from the constants.json file > then write the getSingleGame method. The URL here tells the program from which URL to get the data it needs.

  • In the SingleGame.js shared component, add a link that takes the user to the single game view page: -1-import the Link component from react-router-dom package; -2- in the render, declare a singleGameLink variable for the URL path to the single view; -3- in the HTML, add a Link component with the to attribute being equal to the singleGameLink variable you just defined for the single view path.

  • In components > pages > create a SingleGameView folder with corresponding js and scss files. In the js file:

  1. Declare the state object with a selectedGame as an empty object and a selectedGameId as the id we grab from this.props.match.params.id (aka the id in the url).
  2. Write a buildSingleView function that: -1- declares the gameId object and grabs the id from state; -2-calls the getSingleGame function from the gamesData.js file and passes in the gameId; -3-in the then(), takes the response and sets the state for the game object to the response.data coming back.
  3. Add componentDidMount() method, which grabs the gameId again from state and calls the buildSingleView function.
  4. In the render here: set the game to what is in state.
  5. In the return, write out the html for how the single game view page should look and the product data that should get displayed here.
  6. Add a Close button that is actually a link that takes the user to the Games page.

MVP: Filter by metadata on Games page

User Story

As a user, I want to filter games on the Games page by recommended Age, Instrument, and Prework Level.
As a user, I want to filter games on the Games page by any ONE of these metadata filters AND by MULTIPLE at the same time.
As a user, I can clear one or all of the filters I have applied.

Dev Notes

Note: Decided to use checkboxes in order to allow users to select multiple values for each filter.

SQL

  1. Copy the query I wrote for the get active games method.
  2. Join the GameAge, GameInstrument tables. Should already have the PreworkLevels table joined from the get all active games query.
  3. Add conditions to the sql query to filter it based on age ids / instrument ids / and prework level ids provided - SINCE THESE IDS WILL COME IN IN A STRING FROM THE FRONT END , we need to use the IN statement to say "get all games that have an age id found in this list": WHERE AgeId IN @selectedAges. For testing purposes: WHERE AgeId IN (1, 2, 3) - BUT WHEN REMOVING THEE HARD_CODED values and copying this to the project - > BE SURE TO REMOVE THE parentheses. Dapper doesn't like the parentheses around the variable!!

API

  1. In the GamesController > add a new method to get a filtered list of games. SINCE WE ARE PASSING IN the arrays of selected ages, etc. from the front end - > we will just get them directly from there instead of actually passing them via the new HTTP method attribute that will call this method from the repo > to do that we use the attribute [FromQuery] before each parameter when we call the corresponding repo method!!!
  2. In the GameWithMetadataRepository > add the corresponding new method for getting a filtered list of games: THIS CAN BE a copy of the get all active games method - with some changes: -1 - this method takes in 3 lists of int; -2- it also checks to see if there are any values in them and if not, it pushes all the values for the respective table into these variable (because otherwise, blank means none!!); -3- define the parameters in the parameters variable.

React frontend

  • FIRST: Build the filters:
  1. In the Games.js component > declare an empty array for each list of filter values: agesList; instrumentsList; preworkLevelsList.
  2. Write the functions to call the appropriate data functions that get these values from the database - and then use the response to set the state for those arrays. (write the data functions if not already there in the data file!!!)
  3. Call these functions when the page loads - in componentDidUpdate().
  4. Inside the render: get these arrays from state and write functions to build each set of checklists - map over the array and then call a shared component that has the html for how to display each checkbox. In each component - pass in the actual individual object from the array AND a changeX function as the onClick which gets triggered when that checkbox value is changed. BECAUSE WE CANNOT PASS PROPS FROM THE CHILD COMPONENT TO THE PARENT COMPONENT - WHAT WE NEED TO DO IS "LIFT THE STATE" OF THIS FUNCTION!! Instead of defining the changeX function in each shared component, we define it here, in the parent component and pass it down. That way, any changes it triggers in the child component get passed to the parent component!!! Brilliant! See section below for writing the change functions.
  5. In the return > call the functions you just wrote that build out the checkboxes.
  6. Write those shared components!! In each one be sure to -1- import proptypes ; -2- grab the props that are coming in - such as the object we will use to display the checkbox name and the onClick function we passed down. Define the onClick with the passed in onClick in the input tag as well as the value of the input.
  7. Last, to make sure we clear the checkboxes when we click the Clear All button on the Games page > we add a filterCheckbox class on each input for the checkbox. THEN > go back to Games.js > install jquery and import it in this file > write t clearCheckboxes function, which uses jquery to select all elements with the filterCheckbox class and set their checked value as false so that we can make sure the UI matches what the user is trying to do!! LAST : when clearing the checkboxes, we will also set the selected arrays (see section below) to empty so that state matches what the use just did (since the user just clicked Clear All filters!!).
  • NEXT: Grab the values the user selects for each filter and pass them to the backend:
  1. In the Games.js component > declare in state 3 empty arrays for the selected values from these initial arrays to filter by - set to empty in state. Also declare a filteredGamesList empty array.
  2. Write a getFilteredGames function that grabs the 3 arrays for selected values we just declared in state from state and then passes them as arguments in the corresponding function from the data file (write the function if not there yet!! make sure the end point matches the method on the backend!). In the then - so if the get is successful, we set the state for the filtered list of games in state AND for the gamesList - since the latter is the one we actually build the page off of!
  3. Write the change functions that we will then pass down to each filter component: -1- this is a click event so it takes e as an argument; -2- grab the respective selected array from state; -3- if e.target.checked is true than add the value of the target to this array; -4- if it's false, then we want to remove this filter id from the selected array if it is there so we find the index for that id and if it exists, we splice it out of the array and then set the state for the selected array so it does not include that id and get the filtered games list again in order to refresh the page.
  4. To refresh the page every time the user selects a check box > we will use the ComponentDidUpdate() method form React - so of the state of the selected arrays changes, then we get the filtered list!!
  5. In the gamesData.js file > write the getFilteredGames function - which: -1- takes the 3 selected arrays as arguments; -2- is a new promise; -3- makes an axios call to the appropriate end point on thee backend; -4-PASSES IN EACH value form the array int he URL with & relationship!! Be sure toe export it.

MVP: Login

User Story

As a user, I can log in by entering my email and password.

Dev Notes

  • React project > In the Login.js file:
  1. Declare the user object - with email and password properties - in state;
  2. write the login click event (which uses the login function we wrote in the authData.js file - to which we pass the user object from state)
  3. Write the change functions that grab the changes from the fields for email and password below and set them in state.
  4. Write the html for the login fields - be sure to have an id, value and an onChange tag that calls the change function you wrote above
  5. Add the click event to the Log In button

MVP: See all my practices page

User Story

As an authenticated user, I can see the practice plans I have created.

Dev Notes

SQL

  1. Write a sql query to get the practice plans for a specific user uid - join PracticePlans table with Users table with PracticePlanGames table to get all the info we need. Make sure it also filters by a practice plan's active status! only active data!

API

  1. In the PracticePlanRepository file f> write the method to get all the practice plans for a given user uid.
  2. In the PracticePlansController > make sure the class inherits from the FirebaseEnabledController class (so that we can leverage the User_Id property captured by this class) ; write the method to get practice plans by user uid - which does NOT take any arguments!! / BE SURE TO pass in this UserId variable to the repo method you wrote in step 1 above and that you will call in this method.

React project

  1. In helpers > data > create a practicePlansData.js file and write the viewUserPracticePlans function, which is a new Promise and does an axios call to get all the practice plans for this user uid from our database /from the api/practiceplans/uid endpoint.
  2. In src > components > pages > create a PracticePlans folder with .js and .scss files.
  3. In the PracticePlans.js > declare a state object and in it, declare an empty array of practiceplans.
  4. Import the practicePlansData.js file.
  5. Write the getPracticePlansByUid method that calls the corresponding method from the practicePlansData.js in order to get the array of practiceplans for this user - this is a Promise, so it takes a .then a(where we set the state for the array of practicePlans) and a .catch (where we console the error).
  6. Add the componentDidMount() method, which calls the getPracticePlansByUid method upon load.
  7. In the render here > declare the userPracticePlans object that takes the value from state; - write a buildPracticePlans function that takes the practiceplans' array and maps over it and for each plan, calls the PracticePlanItem component, uses a key attribute equal to practicePlan.id and also has a practicePlan attribute equal to the practicePlan object.
  8. In the return here > add the html for the page and the div for the practiceplans. MAYBE DISPLAY THEM IN A GRID???? Use reactstrap grid then!!
  9. In src > components > shared > create a PracticePlanItem folder with .js and .scss files.
  10. In the PracticePlanItem.js file > define the HTML for how each practice plan should look and display the required metadata.
    In the PracticePlans.js file > import the new PracticePlanItem.js file.

SQL: Create SQL database / tables

User Story

As a developer, I want to see the scripts to create the SQL tables for this project.

Dev Notes

*** See the SQLQuery... file in the project folder!!***

  1. In SSMS, go to Databases > right-click > New database.
  2. Create a new query.
  3. In the query, write the code to create the table.
  4. Define primary keys. See example below.
  5. Define foreign keys. See example below.

Example for creating a database table and adding primary key and foreign key at the same time:

Sample script for creating a table and foreign key relationships / equivalent for using the UI to do the same (from BoardAndBarber sql file done in class):

CREATE TABLE Payment (
	paymentId int identity(1,1) Primary Key not null,
	paymentType varchar(20) null,
	appointmentId int not null,
	discount int not null,
	payer varchar(200) not null,
	FOREIGN KEY(appointmentId) REFERENCES Appointment(appointmentId)
);

Add backend configuration

As a user, I want to use the configuration available in ASP .NET Core - such as for connection strings.

API: Write basic APIs to get all / get single / post / put data - as appropriate - for each collection!

As a user, I want to GET a list of all items in a collection, GET a single item, POST a new item, and PUT/update an existing item.

Updates:

As of Dec 1, I had:

  • GET ALL for Ages, Instruments, GameIcons, PreworkLevels, Users, Games
  • GET SINGLE for Games

Collections

  • MVP:
  1. Games
  2. Users
  3. UserPracticePlas
  4. UserPracticePlanGames
  • STRETCH:
  1. UserReviews
  2. UserPhotos
  3. UserRating
  4. UserChildren

Dev Notes for all the collections listed above:

Prep:

  1. If not yet done: Install Microsoft.Data.SqlClient and Dapper (In VS > Solution Explorer > right-click on project name > Browse tab > search for each program and install each one).
  2. Add the Model folder for the collection if not already done.

Dev Notes for the GetAll method:

Repository file:

  1. Create the Repository file for each collection: In VS > Data folder > right-click > Add class.
  2. Add the using statements for Microsoft.Data.SqlClient and Dapper in this file.
  3. Declare / instantiate the static list of records (for ex, games).
  4. Declare the const string variable for the database connection info.
  5. Create the GetAllGames() method - which is public and has a return type of IEnumerable<Game> since we want to get back a list of all the games that we can then display. This method:
  • declares a new variable for the database we are accessing that refers to the connection info declared above and uses the using prefix;
  • declares a new variable that stores the sql query for the data we want to return;
  • declares a variable for the result of the query;
  • finally, it returns that result (allGames in our case).

Controller file:

  1. Create the Controller file: Controllers folder > right-click > Add > Controller > Select API Controller - Empty > Name it (plural collection name + Controller.cs)
  2. Change the Route to be more explicit about the URL you want to use:api/games
  3. Add using statements for the data and models folders, if not there already.
  4. Instantiate a new repo based on the repository file for this collection since we will be referencing it in every method.
  5. Write the [HttpGet] method to get all the records for the collection (games in this case). This method:
  • is public;
  • has a return type of IActionResult (so that we can return the specific status code for the result as well as the data);
  • declares a new variable for all the games returned (which is the result of calling the new GetAllGames() method we wrote in the Repository file above);
  • returns the Ok code and the new variable for the IEnumerable of games.

To test:

  1. Build and run the app.
  2. Open Postman.
  3. Select GET as the type.
  4. Copy the right URL into Postman and add ‘api/games’
  5. Click Send.
  • Expected result: You should see a list of all the games currently in your database.

Dev Notes for GetSingleGame() action:

Repository file:

  1. Write the GetGameById(int id) method - which is public and has a return type of Game since we want to get back a single record for the game the user selected. This method:
  • declares a new variable for the database we are accessing that refers to the connection info declared above and uses the using prefix;
  • declares a new variable that stores the sql query for the data we want to return; when you write the query: make sure the parameter’s name used in the where statement matches the parameter name for that field in the database!! NOTE: For the GetSingleUser method / or any methods that get data for a single user (practices, submissions, profile): --THE SQL QUERY SHOULD USE THE UID TO FIND THE RIGHT USER RECORD FOR THE DATA (since that's what we will have access to on the front end when the user is logged in)
  • declares a parameters variable that lists all the parameters used
  • declares a variable for the return value (selectedGame in our case) - here, you can choose the QueryFirstOrDefault method, which takes in 2 arguments in our case - the query it calls and the parameters it uses in the query.
  • finally, it returns that result (selectedGame in our case).

Controller file:

  1. Write the [HttpGet("{id}")] method to get the records requested by the user. This method:
  • is public;
  • has a return type of IActionResult (so that we can return the specific status code for the result as well as the data);
  • declares a new variable for the single game returned (which is the result of calling the new GetGameById(int id) method we wrote in the Repository file above, which takes in the id parameter);
  • adds an if statement to validate if the id requested does not exist.
  • returns the Ok status code and the selectedGame record.

To test:

  1. Build and run the app.
  2. Open Postman.
  3. Select GET as the type.
  4. Copy the right URL into Postman and add ‘api/games/1’ - you can change the 1 to 2 or 3 to get each of those games.
  5. Click Send - Expected result: You should see the details of the theme requested (ID and name).
  6. Change the ID in the URL to an ID you know does not exist.
  7. Click Send - Expected result: You should see the error message informing you that we did not find the game with that ID.

Dev Notes for AddGame() action:

  1. In the Repository file > Write the AddGame method - which:
  • returns a game record
  • takes in a parameter of the Game type that we can call gameToAdd
  • declares a variable for the sql script to run to insert a new record and output the id assigned by the sql database
  • declares a variable for the connection info
  • declares a variable for the new id that was assigned by the sql database - using the ExecuteScalar method
  • THEN:
  • declares a new variable that stores a select query that looks for the record with a certain ID (using a where statement)
  • declares a new parameters variable that stores the Id that needs to get passed in to the sql query created in the step above - and makes the id parameter the same as the newId identified earlier via the ExecuteScalar method
  • declares a newGame variable that returns the game found when that last query is run with the parameters specified in the parameters variable
  • returns that newGame
  1. In the Controller > Add the CreateGame method, which:
  • is public and returns an IActionResult
  • declares a new variable that stores the variable returned by the AddGame method from the Repository
  • returns the Created status with 2 arguments: the url for the new game and the new game record

Dev Notes for Delete a Game / Technically inactivate a game action:

  • Note: Instead of an actual delete, we will inactivate a game => so we will update a game's IsActive property to false/0
  1. In the Repository file, write the Update method - which:
  • is public and has a return type of Game
  • declares a variable that stores the query that updates the record based on the ID provided (using the set sql method)
  • declares the db variable which has the using prefix and stores the db connection info
  • declares the parameters variable that includes a list of all the parameters needed for the sql query
  • declares the updated game variable that stores the updated record
  • and returns the last value
  1. In the Controller file, write the UpdateGame method - which:
  • takes 2 arguments: thee id it should update and the new record it should use to replace the old one
  • declares a new variable for the updated record that comes back when you run the Update method from the Repository;
  • adds an if statement to validate that the ID provided is a real game ID - using the GetGameById() method from the Repository file
  • returns the Ok status and the updated game record

REPEAT steps above for each collection!

Dev Notes for Joint Collections

  • THEN - To get the related UserPracticePlanGames records displayed in the API call to get a single practice plan:
  1. In the PracticePlanRepository file > Write a new method called GetPracticePlanByIdWithGames(int id). This method: 1-declares the variable for the db connection; 2-declares a variable that stores the sql query to get the practice games for the practice id passed in as the parameter for this method; 3-declares a variable for the parameters for this sql query (and the next query actually!!); 4-declares a variable called practiceGames that stores the UserPracticePlanGames records that are returned when you run this sql query for the practice plan ID passed in; 5-declares a variable that stores the sql query to get the details of a single practice plan based on the practice plan id passed in as the parameter for this method; 6-declares a variable for the selected practice plan returned by the second query; 7-assigns the practiceGames list to the PracticeGames property on the selected order; 8 - finally returns the selected order.
  2. In the PracticePlansController file > change the method called inside the GetPracticePlanById(int id) HttpGet method there to use the new GetPracticePlanByIdWithGames(int id) method defined in the step above.

REPEAT step above for UserPracticePlans joint collection and how that data gets displayed when looking at a user's Practices page!

Have user return to origin place when going to the single game view page from different places

User Story

As a user, whenever I see a game on a page in the app and go to the Single Game View page,
Then, when I click Close on the Single Game View page, I want to be returned to the page where I clicked on the hyperlink.

Dev Notes

React project:

  • TO CONTROL WHERE THE USER RETURNS AFTER VIEWING THE GAME DETAILS PAGE:
  • In each of the files where we originate the View Game action: Games.js, Submissions.js, SinglePracticePlanView, PracticePlanEdit.js files :
  1. Add a variable currentpath with a null value to state.
  2. In the componentDidMount() method > when setting the state of the item, also set the state for the currentpath object with data from props (this.props.location.pathname) - which saves the url of this page.
  3. Inside the render, add the object { currentpath } and pull its value from state.
  4. Declare a new const variable called newroute that has 2 variables: one called pathname (MUST BE THIS LABEL) that is set to the url where we want the user to go when clicking View Game (/${gameId}), and a second one to something we called previouspath that takes the value from the currentpath object. --THIS IS what we have to do to pass the older path props in a Link react component!
  5. Below > inside the return > inside the div > In the Link tag for the View Game button > change the to attribute to the newroute object defined above. -THIS IS WHERE WE ARE PASSING IT TO THE Single Game Details COMPONENT.
  • In SingleGameView.js:
  1. Define proptypes for the previouspath object coming in.
  2. In the closeGameDetails function here > get the previouspath object from props (const { previouspath } = this.props.location;) and then change the current statement telling the app where to go after this view gets closed into a ternary that decides whether it should go to the Games page or one of the other pages:
previouspath === '/games' ? this.props.history.push('/games') : this.props.history.push(previouspath.currentpath);
  • Lastly, we need to pass in the current path for items when user is coming from the Games page - but the user is actually coming in from the game item in that case, so > in the GameItem.js file:
  1. Add state and set the currentpath to '/stuff' in state.
  2. Then, inside the render but before the return, declare an object that extracts the currentpath object value from state.
  3. Then - just like in SingleGame.js - Declare a new const variable called newroute that has 2 variables: one called pathname (MUST BE THIS LABEL) that is set to the url where we want the use to go when clicking View Game Details ( /${gameId} ), and a second one to something we called previouspath that takes the value from the currentpath object. --THIS IS what we have to do to pass the older path props in a Link react component!
  4. LAST: Below > inside the return > inside the div > In the Link tag for the View Game Details button > change the to attribute to the newroute object defined above. -THIS IS WHERE WE ARE PASSING IT TO THE Single Game Details COMPONENT.

Backend/frontend: Basic project setup

Dev Notes

Backend:

  • Visual Studio: Create a new project:
  1. Template: ASP.NET Core Web Application
  2. Project name
  3. ASP.NET Core-specific questions: API
  4. Add a Models folder and add files in it for the data collections
  5. Create a new DataAccess folder that controls the methods to get data from the API/collections
  6. Note: You should already have a Controllers folder. If not, create it.
  7. Github: Create new repository - ADD GITIGNORE file!!!!! (VisualStudio template)
  8. Github > Code: Make sure the key selected at the top is the SSH key > Copy the 2 lines in middle section (git remote add origin ….; git push -u origin master )

  9. GitBash > cd into your new folder > copy those 2 lines in your terminal
  10. Install Microsoft.Data.SqlClient (via NuGet = .Net’s versions of npm / for the .NET ecosystem): In Visual Studio > right-click on a project > Manage NuGet Packages > we installed Microsoft.Data.SqlClient - which allows us to use ADO.Net and the sql connection
  11. Install Dapper = at its core is extending the built-in functionality of SQL connections: In Visual Studio > right-click on a project > Manage NuGet Packages > Type in Dapper and install it / Or you can type in the Package Manager Console: install dapper ….

React:

  1. GitBash: Open Git Bash from the solution folder!!!! > in Git Bash / in the solution folder > Create a react app: npx create-react-app <NAME.ui> -- Suggestion: name this folder the same name as the app but then add .ui or .frontend to distinguish it between it and the rest fo the folders - in our class example: npx create-react-app boardandbarber.ui
  2. GitBash: Create and check out a new ars-setup branch.
  3. Visual Studio Code: complete the usual setup steps.

Connect frontend and backend:

  1. Backend > Startup.cs: Set up CORS.

STRETCH: Free-text search by keyword

User Story

As a user, I can do a free-text search of the games to filter by keyword.

Dev Notes

React frontend:

  • Games.js file:
  1. In the return, add the HTML for the input text field for the Search word (be sure to add a value tag - which uses the variable searchInput - and an onChange function to it) and and the button that triggers the search (be sure to add an onClick tag which can use the getFilteredGames function already written above).
  2. Add the searchInput variable in state.
  3. Add the searchInput variable also to the clearCheckboxes function so that the field is set to empty ('') when the user clears filters.
  4. Include (get it from state) the searchInput variable in the getFilteredGamesList function and pass it as a parameter to the corresponding function from the data file.
  5. Write the changeSearchInput function - which is the onChange function called in this input field - which resets the value in state for the searchInput variable to the e.target.value grabbed by this function.
  • gamesData.js file:
  1. Add the new searchInput parameter to the getFilteredGames function here.

APIs/backend:

  1. In the GamesController file > update the GetFilteredGames method to also take in a searchInput string parameter FROM QUERY!!!. NOTE: We do not want to pass it in the URL because there may not be a search word sometimes and that would break that call!!! If we specify the parameter in the URL/end point for the method, then it has to have a value - so in the case when the user just searches by metadata and not a keyword search, then it will break!
  2. Also pass it in the corresponding method you call from the repo.
  3. In the Repository file > add the searchInput string as a parameter in the corresponding method.
  4. Then, add it in the sql query like this: as a new condition for where so: AND games.Keywords like '%' + @searchInput + '%'.
  5. BE SURE TO USE A null coalescing operator in the parameters definition like this: var paremetersForFilters = new { ....., searchInput = (searchInput ?? "") }; - which says: for the searchInput variable, use the value it provides and if there isn't one, use an empty string - BECAUSE otherwise, it would use NULL - which is NOT the same as an empty string - which is really "this doesn't exist" an thus would break the query.

React: Set up navbar and routing

User Story

As a user, I can navigate via navbar links or buttons to the following pages when I am not logged in the app and see the URL change accordingly whenever I am on a different page: Home, Games -- and when I am logged in, I can navigate to the following pages and see the URL change accordingly: Practices, Profile, Submissions.

AC

NAVBAR AC:

WHEN I go to the login page,
THEN I will be on the Home page,
AND the navbar should display links to Home, Games, and Log In,
AND when I log in,
THEN the navbar should display links to Home, Games, Practices, Submissions, Profile, and Log Out.

Routing AC:

WHEN I log in, I should be on the Home page, and the url should display '/home',
THEN when I click on the Games navbar item, I should go to the Games page, and the url should display '/games',
THEN when I click on the Practics navbar item, I should go to the Practices page, and the url should display '/practices',
THEN when I click on the Submissions navbar item, I should go to the Submissions page, and the url should display '/submissions',
THEN when I click on the Profile navbar item, I should go to the Profile page, and the url should display '/profile'.

Dev Notes

  • In components > pages: Create all the components we need for the pages in this app > pages folder: Home; Games; SingleGame, Practices, Submissions, Profile - with corresponding js and scss files and create classes and components in each file respectively.
  • App.js > import all the files you just created here.
  • Install React Router DOM. (npm install react-router-dom)
  • App.js > after the firebase imports, import 4 different components from the React Router DOM library: BrowserRoute, Route, Redirect, Switch.
  • App.js: Add the objects for Route and PrivateRoute at the top after the fbConnection and before the class component.
  • App.js > inside the html div in the return > Add the BrowseRouter > Inside the BrowserRouter tag > add React.Fragment tag > Inside React.Fragment > add the MyNavbar tag - and it should include the authed attribute > then add a div with a class of container and another div with a class of row > then define the content for the route here — use the Switch component: -1-Add a private route for all the pages inside the app (Practices, Submissions, Profile); -2-Add a route for the Auth/Home page and for the Games and SingleGame pages ; -3-add a Redirect component for any incorrect url the user types in on this site.
  • In MyNavbar.js file: Update the navbar to use the correct paths for its menus:
  1. Install reactstrap - if not yet installed;
  2. Copy Navbar code from reactstrap into the div in the return in the MyNavbar.js component;
  3. Import the reactstrap navbar components at the top of the MyNavbar.js file (after the React package import);
  4. import NavLink - as an RRNavLink from the react-router-dom package here, too;
  5. update code for isOpen and toggle hooks from reactstrap navbar code;
  6. IN APP.JS: I should already be passing in authed to the MyNavbar component.
  7. BACK in MyNavbar.js: Add static propTypes for the authed props.
  8. Control what user sees in navbar before logging in and after: Inside the render, before the return, write a buildNavbar function that uses the authed variable and when it is true, it builds the navbar and uses the NavLink component with a tag of RRNavLink for each navbar item, and include the appropriate paths for each in a to attribute here; when authed is false, the navbar includes the Home, Games and Log In Links. So > copy the loginClickEvent function from the Auth.js file here and delete the firebase imports in Auth.js; add the NavItem for the Log In button and the onClick event handler in the link tag with the loginClickEvent function I just added above.
  9. Delete the default href attributes in the Links.
  10. call this buildNavbar function inside the navbar reactstrap code - inside the Collapse tag there.
  11. Add a CSS class that makes the hand pointer when user hovers over the nav item for the Log Out link (pointer is not displayed by default).(create the class in the MyNavbar.scss file and then add it here for the Log Out and Log In link tags).
  • In Games.js: import the name Link component from the react-router-dom package and add a Link tag on the Games page to navigate to the Single Game page. Define add the to attributes with the url where they should take the user.

Date validations

  1. Practice plan end date must be the same or after practice plan start date,
  2. Practice plan game date must be the same as the start date or after it AND the same as the end date or before it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.