Skip to content

Commit

Permalink
feat: can now schedule meetings as a user
Browse files Browse the repository at this point in the history
Barebones Vue front end has been implemented, and allows for candidates to schedule a meeting time based on the meeting's hosts' preferences.
  • Loading branch information
drewfugate authored Mar 27, 2024
1 parent 5aa6c45 commit 767e773
Show file tree
Hide file tree
Showing 41 changed files with 12,899 additions and 308 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: CI
on:
push:
branches:
- '**'
- "**"
pull_request:
branches:
- '**'
- "**"
jobs:
lint:
runs-on: ubuntu-latest
Expand All @@ -15,6 +15,8 @@ jobs:
with:
go-version: 1.22
- uses: golangci/golangci-lint-action@v3
with:
working-directory: ./backend
test:
runs-on: ubuntu-latest
needs: lint
Expand All @@ -24,6 +26,8 @@ jobs:
with:
go-version: 1.22
- run: go test -v ./...
working-directory: ./backend

release:
runs-on: ubuntu-latest
needs: test
Expand All @@ -38,4 +42,4 @@ jobs:
with:
hooks: goreleaser
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Binary executable
/build/
dist

# Dependency directories
/node_modules/
node_modules

# Environment files
.env
Expand All @@ -15,6 +16,13 @@
.idea/
*.swp
*.swo
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/dist


# Log files
*.log
Expand Down
17 changes: 14 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@ FROM golang:1.22 as builder

WORKDIR /neverl8/

COPY go.* ./
RUN go mod download
COPY /backend/go.* ./
COPY /backend/ ./

COPY . ./
RUN go mod download

RUN CGO_ENABLED=0 GOOS=linux go build -v -o neverl8

FROM node:latest as frontend-builder

WORKDIR /neverl8/

COPY /frontend/package.json /frontend/package-lock.json* ./
RUN npm install

COPY /frontend/ ./
RUN npm run build

FROM alpine:latest
RUN apk --no-cache add ca-certificates

WORKDIR /root/

# Copy the binary to the production image from the builder stage.
COPY --from=builder /neverl8 .
COPY --from=frontend-builder /neverl8/dist /root/frontend

# Run the web service on container startup.
CMD ["./neverl8"]
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# neverl8

**neverl8** is a streamlined Go application utilizing the Go-Chi router, GORM ORM, showcasing calendar operations within a PostgreSQL environment. Designed for simplicity and efficiency, this project will serve as the essential scheduler for Rise8.
**neverl8** is a streamlined Go application utilizing the Go-Chi router, GORM ORM, Postgres, and a Vue frontend utilizing Vuetify components. Designed for simplicity and efficiency, this project will serve as the essential scheduler for Rise8.

## Prerequisites

Expand All @@ -9,6 +9,7 @@ To get the most out of **neverl8**, please ensure you have the following install
- **Go programming language** (version 1.16 or higher) for the backend logic.
- **PostgreSQL database** for data persistence.
- **Git** for version control and collaboration.
- **Docker** for server hosting

## Installation

Expand All @@ -21,7 +22,7 @@ Follow these simple steps to get **neverl8** up and running on your machine:
2. **Navigate to the project directory**.
3. **Launch the application**:
```bash
go run main.go
docker-compose up --build
```
Congratulations! The application should now be accessible at http://localhost:8080.

Expand All @@ -47,12 +48,24 @@ With these steps completed, pre-commit hooks will automatically execute on every
**neverl8** also incorporates `golangci-lint` for enforcing Go best practices and code styles. To use `golangci-lint` in your development process:

1. **Install golangci-lint** on your local machine. You can follow the [official golangci-lint installation instructions](https://golangci-lint.run/usage/install/).
2. Once installed, you can run `golangci-lint run` in the project directory to analyze your code.
2. Once installed, you can run `golangci-lint run` in the project backend directory to analyze your code.

### Frontend

**neverl8** utilizes Vue for its frontend.
**To view and edit the frontend** navigate to the frontend folder from root and type:

```bash
npm install
npm run serve
```

## Testing

**neverl8** embraces testing as a fundamental part of the development process. To run the unit tests and ensure your setup is correctly configured:
**neverl8** embraces testing as a fundamental part of the development process. To run the unit & integration tests and ensure your setup is correctly configured:

```bash
go test ./...
```
This command triggers all the unit tests within the project, verifying the integrity and functionality of your code.

This command triggers all the unit and integration tests within the project, verifying the integrity and functionality of your code.
14 changes: 0 additions & 14 deletions application/routes.go

This file was deleted.

53 changes: 38 additions & 15 deletions application/app.go → backend/application/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
_ "github.com/golang-migrate/migrate/v4/source/file"
"github.com/joho/godotenv"
"github.com/rise8-us/neverl8/cli"
controller "github.com/rise8-us/neverl8/controller"
"github.com/rise8-us/neverl8/repository"
hostSvc "github.com/rise8-us/neverl8/service/host"
meetingSvc "github.com/rise8-us/neverl8/service/meeting"
Expand All @@ -23,17 +24,18 @@ import (
)

type App struct {
router *chi.Mux
db *gorm.DB
router *chi.Mux
db *gorm.DB
meetingService *meetingSvc.MeetingService
meetingController *controller.MeetingController
hostService *hostSvc.HostService
}

func New() *App {
app := &App{
router: chi.NewRouter(),
}

app.loadRoutes()

return app
}

Expand All @@ -46,20 +48,15 @@ func (a *App) Start(ctx context.Context) error {
ReadHeaderTimeout: requestTimeout,
}

err := godotenv.Load()
// Load environment variables
envMap, err := LoadEnvVariables()
if err != nil {
log.Fatalf("Error loading .env file %v", err)
return fmt.Errorf("failed to load environment variables: %w", err)
}

// Connect to the database
dbHost := os.Getenv("DB_HOST")
dbPort := os.Getenv("DB_PORT")
dbUser := os.Getenv("DB_USER")
dbPassword := os.Getenv("DB_PASSWORD")
dbName := os.Getenv("DB_NAME")
dbSSLMode := os.Getenv("DB_SSLMODE")
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=%s",
envMap["DB_HOST"], envMap["DB_USER"], envMap["DB_PASSWORD"], envMap["DB_NAME"], envMap["DB_SSLMODE"])

dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=%s", dbHost, dbUser, dbPassword, dbName, dbSSLMode)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return fmt.Errorf("failed to connect to the database: %w", err)
Expand All @@ -69,7 +66,8 @@ func (a *App) Start(ctx context.Context) error {
// Migrate db
m, err := migrate.New(
"file://db/migrations",
fmt.Sprintf(("postgres://%s:%s@%s:%s/%s?sslmode=%s"), dbUser, dbPassword, dbHost, dbPort, dbName, dbSSLMode))
fmt.Sprintf(("postgres://%s:%s@%s:%s/%s?sslmode=%s"),
envMap["DB_USER"], envMap["DB_PASSWORD"], envMap["DB_HOST"], envMap["DB_PORT"], envMap["DB_NAME"], envMap["DB_SSLMODE"]))
if err != nil {
log.Fatal(err)
}
Expand All @@ -81,6 +79,14 @@ func (a *App) Start(ctx context.Context) error {
}
}

meetingRepo := repository.NewMeetingRepository(a.db)
a.meetingService = meetingSvc.NewMeetingService(meetingRepo, nil)
a.meetingController = controller.NewMeetingController(a.meetingService)
hostRepo := repository.NewHostRepository(a.db)
a.hostService = hostSvc.NewHostService(hostRepo)

a.loadRoutes()

// Channel to signal server startup
serverStarted := make(chan struct{})

Expand Down Expand Up @@ -130,3 +136,20 @@ func (a *App) Start(ctx context.Context) error {

return nil
}

func LoadEnvVariables() (map[string]string, error) {
err := godotenv.Load()
if err != nil {
return nil, err
}

envMap := make(map[string]string)
envMap["DB_HOST"] = os.Getenv("DB_HOST")
envMap["DB_PORT"] = os.Getenv("DB_PORT")
envMap["DB_USER"] = os.Getenv("DB_USER")
envMap["DB_PASSWORD"] = os.Getenv("DB_PASSWORD")
envMap["DB_NAME"] = os.Getenv("DB_NAME")
envMap["DB_SSLMODE"] = os.Getenv("DB_SSLMODE")

return envMap, nil
}
31 changes: 31 additions & 0 deletions backend/application/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package application

import (
"log"
"net/http"
"os"
"path/filepath"

"github.com/go-chi/chi/middleware"
)

func (a *App) loadRoutes() {
a.router.Use(middleware.Logger)
a.router.Get("/helloworld", func(w http.ResponseWriter, _ *http.Request) {
_, writeErr := w.Write([]byte("Hello, World!"))
if writeErr != nil {
log.Printf("Error writing response: %v", writeErr)
}
})

a.router.Get("/api/meetings", a.meetingController.GetAllMeetings)
a.router.Get("/api/meeting", a.meetingController.GetMeetingByID)
a.router.Get("/api/meeting/time-slots", a.meetingController.GetAvailableTimeBlocks)
a.router.Post("/api/meeting/schedule", a.meetingController.UpdateMeetingTime)

// Handle frontend route
workDir, _ := os.Getwd()
filesDir := http.Dir(filepath.Join(workDir, "frontend"))
a.router.Handle("/", http.FileServer(filesDir)) // Serve index.html at root
a.router.Handle("/*", http.StripPrefix("/", http.FileServer(filesDir))) // Serve static files
}
12 changes: 7 additions & 5 deletions cli/cli.go → backend/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ func NewCLI(meetingService *meetingSvc.MeetingService, hostService *hostSvc.Host

func (c *CLI) CreateMeetingFromCLI() {
// Create new Hosts
layout := "15:04"
startTime, _ := time.Parse(layout, "14:00") // UTC
endTime, _ := time.Parse(layout, "22:00")
startTime2, _ := time.Parse(layout, "16:00")
endTime2, _ := time.Parse(layout, "22:00")
hosts := []model.Host{
{HostName: "Host 1", TimePreferences: []model.TimePreference{{HostID: 1, StartWindow: "09:00", EndWindow: "17:00"}}},
{HostName: "Host 2", TimePreferences: []model.TimePreference{{HostID: 1, StartWindow: "11:00", EndWindow: "15:00"}}},
{HostName: "Host 1", TimePreferences: []model.TimePreference{{HostID: 1, StartTime: startTime, EndTime: endTime}}},
{HostName: "Host 2", TimePreferences: []model.TimePreference{{HostID: 2, StartTime: startTime2, EndTime: endTime2}}},
}
host1, _ := c.hostService.CreateHost(&hosts[0])
host2, _ := c.hostService.CreateHost(&hosts[1])

meetingDuration := 60
// New Meeting to be created
newMeeting := &model.Meetings{
CandidateID: 2,
Expand All @@ -37,8 +41,6 @@ func (c *CLI) CreateMeetingFromCLI() {
Title: "Example Session",
Description: "Discuss the future of NeverL8",
HasBotGuest: false,
StartTime: time.Now(),
EndTime: time.Now().Add(time.Minute * time.Duration(meetingDuration)),
Hosts: []model.Host{*host1, *host2},
}

Expand Down
Loading

0 comments on commit 767e773

Please sign in to comment.